Merged revisions 321,327,330,332,334,342-343,352-353,355-358,361-363,365,372 via...
[zxing.git] / android / src / com / google / zxing / client / android / DecodeThread.java
1 /*
2  * Copyright (C) 2008 Google Inc.
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       public void handleMessage(Message message) {
70         switch (message.what) {
71           case R.id.decode:
72             captureAndDecode();
73             break;
74           case R.id.save:
75             captureAndSave();
76             break;
77           case R.id.quit:
78             Looper.myLooper().quit();
79             break;
80           case R.id.set_decode_all_mode:
81             setDecodeAllMode();
82             break;
83           case R.id.set_decode_1D_mode:
84             setDecode1DMode();
85             break;
86           case R.id.set_decode_QR_mode:
87             setDecodeQRMode();
88             break;
89           case R.id.toggle_tracing:
90             tracing = !tracing;
91             break;
92         }
93       }
94     };
95     Looper.loop();
96   }
97
98   public void setCameraThreadHandler(Handler cameraThreadHandler) {
99     this.cameraThreadHandler = cameraThreadHandler;
100   }
101
102   private void setDecodeAllMode() {
103     hints = null;
104   }
105
106   // TODO: This is fragile in case we add new formats. It would be better to have a new enum
107   // value which represented all 1D formats.
108   private void setDecode1DMode() {
109     hints = new Hashtable<DecodeHintType, Object>(3);
110     Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>();
111     vector.addElement(BarcodeFormat.UPC_A);
112     vector.addElement(BarcodeFormat.UPC_E);
113     vector.addElement(BarcodeFormat.EAN_13);
114     vector.addElement(BarcodeFormat.EAN_8);
115     vector.addElement(BarcodeFormat.CODE_39);
116     vector.addElement(BarcodeFormat.CODE_128);
117     hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
118   }
119
120   private void setDecodeQRMode() {
121     hints = new Hashtable<DecodeHintType, Object>(3);
122     Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>();
123     vector.addElement(BarcodeFormat.QR_CODE);
124     hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
125   }
126
127   private void captureAndDecode() {
128     Date startDate = new Date();
129     Bitmap bitmap = cameraManager.captureStill();
130     // Let the CameraThread know it can resume previews while the decoding continues in parallel.
131     Message restart = Message.obtain(cameraThreadHandler, R.id.decode_started);
132     restart.sendToTarget();
133
134     if (tracing) {
135       Debug.startMethodTracing("/sdcard/ZXingDecodeThread" + methodTraceCount);
136       methodTraceCount++;
137     }
138     boolean success;
139     Result rawResult = null;
140     try {
141       MonochromeBitmapSource source = new RGBMonochromeBitmapSource(bitmap);
142       rawResult = new MultiFormatReader().decode(source, hints);
143       success = true;
144     } catch (ReaderException e) {
145       success = false;
146     }
147     if (tracing) {
148       Debug.stopMethodTracing();
149     }
150     Date endDate = new Date();
151
152     if (success) {
153       Message message = Message.obtain(cameraThreadHandler, R.id.decode_succeeded, rawResult);
154       message.arg1 = (int) (endDate.getTime() - startDate.getTime());
155       message.sendToTarget();
156     } else {
157       Message message = Message.obtain(cameraThreadHandler, R.id.decode_failed);
158       message.sendToTarget();
159     }
160   }
161
162   /**
163    * This is a debugging feature used to take photos and save them as JPEGs using the exact camera
164    * setup as in normal decoding. This is useful for building up a library of test images.
165    */
166   private void captureAndSave() {
167     Bitmap bitmap = cameraManager.captureStill();
168     OutputStream outStream = getNewPhotoOutputStream();
169     if (outStream != null) {
170       bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
171       try {
172         outStream.close();
173       } catch (IOException e) {
174       }
175       Message success = Message.obtain(cameraThreadHandler, R.id.save_succeeded);
176       success.sendToTarget();
177     } else {
178       Message failure = Message.obtain(cameraThreadHandler, R.id.save_failed);
179       failure.sendToTarget();
180     }
181   }
182
183   /**
184    * We prefer to write to the SD Card because it has more space, and is automatically mounted as a
185    * drive over USB. If it's not present, fall back to the package's private file area here:
186    *
187    * /data/data/com.google.zxing.client.android/files
188    *
189    * @return A stream which represents the new file where the photo will be saved.
190    */
191   private OutputStream getNewPhotoOutputStream() {
192     File sdcard = new File("/sdcard");
193     if (sdcard.exists()) {
194       File barcodes = new File(sdcard, "barcodes");
195       if (!barcodes.exists()) {
196         if (!barcodes.mkdir()) {
197           return null;
198         }
199       }
200       String fileName = getNewPhotoName();
201       try {
202         return new FileOutputStream(new File(barcodes, fileName));
203       } catch (FileNotFoundException e) {
204       }
205     } else {
206       Application application = activity.getApplication();
207       String fileName = getNewPhotoName();
208       try {
209         return application.openFileOutput(fileName, 0);
210       } catch (FileNotFoundException e) {
211       }
212     }
213     return null;
214   }
215
216   private String getNewPhotoName() {
217     Date now = new Date();
218     return "capture" + now.getTime() + ".jpg";
219   }
220
221 }