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