Minor style tweaks
[zxing.git] / android / src / com / google / zxing / client / android / DecodeThread.java
1 /*
2  * Copyright 2008 ZXing authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.google.zxing.client.android;
18
19 import android.app.Application;
20 import android.graphics.Bitmap;
21 import android.os.Debug;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Message;
25 import com.google.zxing.BarcodeFormat;
26 import com.google.zxing.DecodeHintType;
27 import com.google.zxing.MonochromeBitmapSource;
28 import com.google.zxing.MultiFormatReader;
29 import com.google.zxing.ReaderException;
30 import com.google.zxing.Result;
31
32 import java.io.File;
33 import java.io.FileNotFoundException;
34 import java.io.FileOutputStream;
35 import java.io.IOException;
36 import java.io.OutputStream;
37 import java.util.Date;
38 import java.util.Hashtable;
39 import java.util.Vector;
40
41 /**
42  * This thread does all the heavy lifting of decoding the images. It can also save images to flash
43  * for debugging purposes.
44  *
45  * @author dswitkin@google.com (Daniel Switkin)
46  */
47 final class DecodeThread extends Thread {
48
49   public Handler handler;
50
51   private final BarcodeReaderCaptureActivity activity;
52   private final CameraManager cameraManager;
53   private Hashtable<DecodeHintType, Object> hints;
54   private Handler cameraThreadHandler;
55   private int methodTraceCount;
56   private boolean tracing;
57
58   DecodeThread(BarcodeReaderCaptureActivity activity, CameraManager cameraManager) {
59     this.activity = activity;
60     this.cameraManager = cameraManager;
61     methodTraceCount = 0;
62     tracing = false;
63   }
64
65   @Override
66   public void run() {
67     Looper.prepare();
68     handler = new Handler() {
69       @Override
70       public void handleMessage(Message message) {
71         switch (message.what) {
72           case R.id.decode:
73             captureAndDecode();
74             break;
75           case R.id.save:
76             captureAndSave();
77             break;
78           case R.id.quit:
79             Looper.myLooper().quit();
80             break;
81           case R.id.set_decode_all_mode:
82             setDecodeAllMode();
83             break;
84           case R.id.set_decode_1D_mode:
85             setDecode1DMode();
86             break;
87           case R.id.set_decode_QR_mode:
88             setDecodeQRMode();
89             break;
90           case R.id.toggle_tracing:
91             tracing = !tracing;
92             break;
93         }
94       }
95     };
96     Looper.loop();
97   }
98
99   public void setCameraThreadHandler(Handler cameraThreadHandler) {
100     this.cameraThreadHandler = cameraThreadHandler;
101   }
102
103   private void setDecodeAllMode() {
104     hints = null;
105   }
106
107   // TODO: This is fragile in case we add new formats. It would be better to have a new enum
108   // value which represented all 1D formats.
109   private void setDecode1DMode() {
110     hints = new Hashtable<DecodeHintType, Object>(3);
111     Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>();
112     vector.addElement(BarcodeFormat.UPC_A);
113     vector.addElement(BarcodeFormat.UPC_E);
114     vector.addElement(BarcodeFormat.EAN_13);
115     vector.addElement(BarcodeFormat.EAN_8);
116     vector.addElement(BarcodeFormat.CODE_39);
117     vector.addElement(BarcodeFormat.CODE_128);
118     hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
119   }
120
121   private void setDecodeQRMode() {
122     hints = new Hashtable<DecodeHintType, Object>(3);
123     Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>();
124     vector.addElement(BarcodeFormat.QR_CODE);
125     hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
126   }
127
128   private void captureAndDecode() {
129     Date startDate = new Date();
130     Bitmap bitmap = cameraManager.captureStill();
131     // Let the CameraThread know it can resume previews while the decoding continues in parallel.
132     Message restart = Message.obtain(cameraThreadHandler, R.id.decode_started);
133     restart.sendToTarget();
134
135     if (tracing) {
136       Debug.startMethodTracing("/sdcard/ZXingDecodeThread" + methodTraceCount);
137       methodTraceCount++;
138     }
139     boolean success;
140     Result rawResult = null;
141     try {
142       MonochromeBitmapSource source = new RGBMonochromeBitmapSource(bitmap);
143       rawResult = new MultiFormatReader().decode(source, hints);
144       success = true;
145     } catch (ReaderException e) {
146       success = false;
147     }
148     if (tracing) {
149       Debug.stopMethodTracing();
150     }
151     Date endDate = new Date();
152
153     if (success) {
154       Message message = Message.obtain(cameraThreadHandler, R.id.decode_succeeded, rawResult);
155       message.arg1 = (int) (endDate.getTime() - startDate.getTime());
156       message.sendToTarget();
157     } else {
158       Message message = Message.obtain(cameraThreadHandler, R.id.decode_failed);
159       message.sendToTarget();
160     }
161   }
162
163   /**
164    * This is a debugging feature used to take photos and save them as JPEGs using the exact camera
165    * setup as in normal decoding. This is useful for building up a library of test images.
166    */
167   private void captureAndSave() {
168     Bitmap bitmap = cameraManager.captureStill();
169     OutputStream outStream = getNewPhotoOutputStream();
170     if (outStream != null) {
171       bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
172       try {
173         outStream.close();
174       } catch (IOException e) {
175       }
176       Message success = Message.obtain(cameraThreadHandler, R.id.save_succeeded);
177       success.sendToTarget();
178     } else {
179       Message failure = Message.obtain(cameraThreadHandler, R.id.save_failed);
180       failure.sendToTarget();
181     }
182   }
183
184   /**
185    * We prefer to write to the SD Card because it has more space, and is automatically mounted as a
186    * drive over USB. If it's not present, fall back to the package's private file area here:
187    *
188    * /data/data/com.google.zxing.client.android/files
189    *
190    * @return A stream which represents the new file where the photo will be saved.
191    */
192   private OutputStream getNewPhotoOutputStream() {
193     File sdcard = new File("/sdcard");
194     if (sdcard.exists()) {
195       File barcodes = new File(sdcard, "barcodes");
196       if (!barcodes.exists()) {
197         if (!barcodes.mkdir()) {
198           return null;
199         }
200       }
201       String fileName = getNewPhotoName();
202       try {
203         return new FileOutputStream(new File(barcodes, fileName));
204       } catch (FileNotFoundException e) {
205       }
206     } else {
207       Application application = activity.getApplication();
208       String fileName = getNewPhotoName();
209       try {
210         return application.openFileOutput(fileName, 0);
211       } catch (FileNotFoundException e) {
212       }
213     }
214     return null;
215   }
216
217   private static String getNewPhotoName() {
218     return "capture" + System.currentTimeMillis() + ".jpg";
219   }
220
221 }