Standardize and update all copyright statements to name "ZXing authors" as suggested...
[zxing.git] / android / src / com / google / zxing / client / android / BarcodeReaderCaptureActivity.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.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.media.MediaPlayer;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Message;
26 import android.view.KeyEvent;
27 import android.view.Menu;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.view.Window;
31 import android.widget.TextView;
32 import com.google.zxing.Result;
33 import com.google.zxing.ResultPoint;
34 import com.google.zxing.client.result.ParsedReaderResult;
35 import com.google.zxing.client.result.ParsedReaderResultType;
36
37 /**
38  * The barcode reader activity itself. This is loosely based on the CameraPreview
39  * example included in the Android SDK.
40  *
41  * @author dswitkin@google.com (Daniel Switkin)
42  * @author Android Team (for CameraPreview example)
43  */
44 public final class BarcodeReaderCaptureActivity extends Activity {
45
46   private CameraManager cameraManager;
47   private CameraSurfaceView surfaceView;
48   private CameraThread cameraThread;
49   private String lastResult;
50
51   private static final int ABOUT_ID = Menu.FIRST;
52   private static final int HELP_ID = Menu.FIRST + 1;
53
54   @Override
55   public void onCreate(Bundle icicle) {
56     super.onCreate(icicle);
57     requestWindowFeature(Window.FEATURE_NO_TITLE);
58
59     setContentView(R.layout.main);
60
61     cameraManager = new CameraManager(getApplication());
62     surfaceView = new CameraSurfaceView(getApplication(), cameraManager);
63     surfaceView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
64         ViewGroup.LayoutParams.FILL_PARENT));
65
66     ViewGroup previewView = (ViewGroup) findViewById(R.id.preview_view);
67     previewView.addView(surfaceView);
68     cameraThread = null;
69
70     // TODO re-enable this when issues with Matrix.setPolyToPoly() are resolved
71     //GridSampler.setGridSampler(new AndroidGraphicsGridSampler());
72   }
73
74   @Override
75   protected void onResume() {
76     super.onResume();
77     resetStatusView();
78     cameraManager.openDriver();
79     if (cameraThread == null) {
80       cameraThread = new CameraThread(this, surfaceView, cameraManager, messageHandler);
81       cameraThread.start();
82     }
83   }
84
85   @Override
86   protected void onPause() {
87     super.onPause();
88     if (cameraThread != null) {
89       cameraThread.quitSynchronously();
90       cameraThread = null;
91     }
92     cameraManager.closeDriver();
93   }
94
95   @Override
96   public boolean onKeyDown(int keyCode, KeyEvent event) {
97     switch (keyCode) {
98       case KeyEvent.KEYCODE_A:
99         cameraThread.setDecodeAllMode();
100         break;
101       case KeyEvent.KEYCODE_C:
102         Message save = Message.obtain(cameraThread.handler, R.id.save);
103         save.sendToTarget();
104         break;
105       case KeyEvent.KEYCODE_P:
106         cameraManager.setUsePreviewForDecode(true);
107         break;
108       case KeyEvent.KEYCODE_Q:
109         cameraThread.setDecodeQRMode();
110         break;
111       case KeyEvent.KEYCODE_S:
112         cameraManager.setUsePreviewForDecode(false);
113         break;
114       case KeyEvent.KEYCODE_T:
115         cameraThread.toggleTracing();
116         break;
117       case KeyEvent.KEYCODE_U:
118         cameraThread.setDecode1DMode();
119         break;
120       default:
121         return super.onKeyDown(keyCode, event);
122     }
123     return true;
124   }
125
126   @Override
127   public boolean onCreateOptionsMenu(Menu menu) {
128     super.onCreateOptionsMenu(menu);
129     menu.add(0, ABOUT_ID, R.string.menu_about);
130     menu.add(0, HELP_ID, R.string.menu_help);
131     return true;
132   }
133
134   @Override
135   public boolean onOptionsItemSelected(Menu.Item item) {
136     Context context = getApplication();
137     switch (item.getId()) {
138       case ABOUT_ID:
139         showAlert(context.getString(R.string.title_about), 0,
140           context.getString(R.string.msg_about),
141           context.getString(R.string.button_ok),
142           true);
143         break;
144       case HELP_ID:
145         showAlert(context.getString(R.string.title_help), 0,
146             context.getString(R.string.msg_help),
147             context.getString(R.string.button_ok), true);
148         break;
149     }
150     return super.onOptionsItemSelected(item);
151   }
152
153   private final Handler messageHandler = new Handler() {
154     @Override
155     public void handleMessage(Message message) {
156       switch (message.what) {
157         case R.id.decode_succeeded:
158           int duration = message.arg1;
159           handleDecode((Result) message.obj, duration);
160           break;
161         case R.id.restart_preview:
162           restartPreview();
163           break;
164       }
165     }
166   };
167
168   void restartPreview() {
169     resetStatusViewColor();
170     Message restart = Message.obtain(cameraThread.handler, R.id.restart_preview);
171     restart.sendToTarget();
172   }
173
174   private void handleDecode(Result rawResult, int duration) {
175     if (!rawResult.toString().equals(lastResult)) {
176       lastResult = rawResult.toString();
177       playBeepSound();
178
179       ResultPoint[] points = rawResult.getResultPoints();
180       if (points != null && points.length > 0) {
181         surfaceView.drawResultPoints(points);
182       }
183
184       TextView textView = (TextView) findViewById(R.id.status_text_view);
185       ParsedReaderResult readerResult = parseReaderResult(rawResult);
186       textView.setText(readerResult.getDisplayResult() + " (" + duration + " ms)");
187
188       TextView actionButton = (TextView) findViewById(R.id.status_action_button);
189       int buttonText = getActionButtonText(readerResult.getType());
190       if (buttonText != 0) {
191         actionButton.setVisibility(View.VISIBLE);
192         actionButton.setText(buttonText);
193         View.OnClickListener handler = new ResultHandler(this, readerResult);
194         actionButton.setOnClickListener(handler);
195         actionButton.requestFocus();
196       } else {
197         actionButton.setVisibility(View.GONE);
198       }
199
200       View statusView = findViewById(R.id.status_view);
201       statusView.setBackgroundColor(0xc000ff00);
202
203       // Show the green finder patterns for one second, then restart the preview
204       Message message = Message.obtain(messageHandler, R.id.restart_preview);
205       messageHandler.sendMessageDelayed(message, 1000);
206     } else {
207       restartPreview();
208     }
209   }
210
211   private void playBeepSound() {
212     MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.beep);
213     mediaPlayer.start();
214   }
215
216   private void resetStatusView() {
217     resetStatusViewColor();
218     TextView textView = (TextView) findViewById(R.id.status_text_view);
219     textView.setText(R.string.msg_default_status);
220     View actionButton = findViewById(R.id.status_action_button);
221     actionButton.setVisibility(View.GONE);
222     lastResult = "";
223   }
224
225   private void resetStatusViewColor() {
226     View statusView = findViewById(R.id.status_view);
227     statusView.setBackgroundColor(0x50000000);
228   }
229
230   private static ParsedReaderResult parseReaderResult(Result rawResult) {
231     ParsedReaderResult readerResult = ParsedReaderResult.parseReaderResult(rawResult);
232     if (readerResult.getType().equals(ParsedReaderResultType.TEXT)) {
233       String rawText = rawResult.getText();
234       AndroidIntentParsedResult androidResult = AndroidIntentParsedResult.parse(rawText);
235       if (androidResult != null) {
236         Intent intent = androidResult.getIntent();
237         if (!Intent.VIEW_ACTION.equals(intent.getAction())) {
238           // For now, don't take anything that just parses as a View action. A lot
239           // of things are accepted as a View action by default.
240           readerResult = androidResult;          
241         }
242       }
243     }
244     return readerResult;
245   }
246
247   private static int getActionButtonText(ParsedReaderResultType type) {
248     int buttonText;
249     if (type.equals(ParsedReaderResultType.ADDRESSBOOK)) {
250       buttonText = R.string.button_add_contact;
251     } else if (type.equals(ParsedReaderResultType.URI) ||
252                type.equals(ParsedReaderResultType.BOOKMARK) ||
253                type.equals(ParsedReaderResultType.URLTO)) {
254       buttonText = R.string.button_open_browser;
255     } else if (type.equals(ParsedReaderResultType.EMAIL) ||
256                type.equals(ParsedReaderResultType.EMAIL_ADDRESS)) {
257       buttonText = R.string.button_email;
258     } else if (type.equals(ParsedReaderResultType.UPC)) {
259       buttonText = R.string.button_lookup_product;
260     } else if (type.equals(ParsedReaderResultType.TEL)) {
261       buttonText = R.string.button_dial;
262     } else if (type.equals(ParsedReaderResultType.GEO)) {
263       buttonText = R.string.button_show_map;
264     } else {
265       buttonText = 0;
266     }
267     return buttonText;
268   }
269
270 }