Small updates and improvements to the Android client.
[zxing.git] / android / src / com / android / barcodes / BarcodesCaptureActivity.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.android.barcodes;
18
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.content.DialogInterface;
22 import android.content.Intent;
23 import android.content.SharedPreferences;
24 import android.content.res.AssetFileDescriptor;
25 import android.content.res.Configuration;
26 import android.media.MediaPlayer;
27 import android.media.AudioManager;
28 import android.media.MediaPlayer.OnCompletionListener;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.os.Message;
32 import android.preference.PreferenceManager;
33 import android.view.KeyEvent;
34 import android.view.Menu;
35 import android.view.MenuItem;
36 import android.view.SurfaceHolder;
37 import android.view.SurfaceView;
38 import android.view.View;
39 import android.view.Window;
40 import android.view.WindowManager;
41 import android.widget.Button;
42 import android.widget.TextView;
43 import com.google.zxing.Result;
44 import com.google.zxing.ResultPoint;
45 import com.google.zxing.client.result.ParsedResult;
46
47 import java.io.IOException;
48
49 /**
50  * The barcode reader activity itself. This is loosely based on the CameraPreview
51  * example included in the Android SDK.
52  */
53 public final class BarcodesCaptureActivity extends Activity implements SurfaceHolder.Callback {
54
55     private static final int SETTINGS_ID = Menu.FIRST;
56     private static final int HELP_ID = Menu.FIRST + 1;
57     private static final int ABOUT_ID = Menu.FIRST + 2;
58
59     public BarcodesCaptureActivityHandler mHandler;
60
61     private ViewfinderView mViewfinderView;
62     private MediaPlayer mMediaPlayer;
63     private String mLastResult;
64     private boolean mPlayBeep;
65     private boolean mScanIntent;
66     private String mDecodeMode;
67
68     @Override
69     public void onCreate(Bundle icicle) {
70         super.onCreate(icicle);
71
72         Window window = getWindow();
73         window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
74         setContentView(R.layout.capture);
75
76         CameraManager.init(getApplication());
77         mViewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
78         mHandler = null;
79
80         SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
81         SurfaceHolder surfaceHolder = surfaceView.getHolder();
82         surfaceHolder.addCallback(this);
83         surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
84     }
85
86     @Override
87     protected void onResume() {
88         super.onResume();
89         resetStatusView();
90
91         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
92         mPlayBeep = prefs.getBoolean(BarcodesPreferenceActivity.KEY_PLAY_BEEP, true);
93         initBeepSound();
94
95         Intent intent = getIntent();
96         if (intent != null && intent.getAction().equals(Intents.Scan.ACTION)) {
97             mScanIntent = true;
98             mDecodeMode = intent.getStringExtra(Intents.Scan.MODE);
99         } else {
100             mScanIntent = false;
101             mDecodeMode = null;
102         }
103     }
104
105     @Override
106     protected void onPause() {
107         super.onPause();
108         if (mHandler != null) {
109             mHandler.quitSynchronously();
110             mHandler = null;
111         }
112         CameraManager.get().closeDriver();
113     }
114
115     @Override
116     public boolean onKeyDown(int keyCode, KeyEvent event) {
117         if (keyCode == KeyEvent.KEYCODE_BACK) {
118             if (mScanIntent) {
119                 setResult(RESULT_CANCELED);
120                 finish();
121                 return true;
122             }
123         } else if (keyCode == KeyEvent.KEYCODE_FOCUS || keyCode == KeyEvent.KEYCODE_CAMERA) {
124             // Handle these events so they don't launch the Camera app
125             return true;
126         }
127         return super.onKeyDown(keyCode, event);
128     }
129
130     @Override
131     public boolean onCreateOptionsMenu(Menu menu) {
132         super.onCreateOptionsMenu(menu);
133         menu.add(0, SETTINGS_ID, 0, R.string.menu_settings)
134                 .setIcon(android.R.drawable.ic_menu_preferences);
135         menu.add(0, HELP_ID, 0, R.string.menu_help)
136                 .setIcon(android.R.drawable.ic_menu_help);
137         menu.add(0, ABOUT_ID, 0, R.string.menu_about)
138                 .setIcon(android.R.drawable.ic_menu_info_details);
139         return true;
140     }
141
142     @Override
143     public boolean onOptionsItemSelected(MenuItem item) {
144         switch (item.getItemId()) {
145             case SETTINGS_ID: {
146                 Intent intent = new Intent(Intent.ACTION_VIEW);
147                 intent.setClassName(this, BarcodesPreferenceActivity.class.getName());
148                 startActivity(intent);
149                 break;
150             }
151             case HELP_ID: {
152                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
153                 builder.setTitle(R.string.title_help);
154                 builder.setMessage(R.string.msg_help);
155                 builder.setPositiveButton(R.string.button_ok, null);
156                 builder.show();
157                 break;
158             }
159             case ABOUT_ID: {
160                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
161                 builder.setTitle(R.string.title_about);
162                 builder.setMessage(getString(R.string.msg_about) + "\n\n" + getString(R.string.zxing_url));
163                 builder.setIcon(R.drawable.zxing_icon);
164                 builder.setPositiveButton(R.string.button_open_browser, mAboutListener);
165                 builder.setNegativeButton(R.string.button_cancel, null);
166                 builder.show();
167                 break;
168             }
169         }
170         return super.onOptionsItemSelected(item);
171     }
172
173     @Override
174     public void onConfigurationChanged(Configuration config) {
175         // Do nothing, this is to prevent the activity from being restarted when the keyboard opens.
176         super.onConfigurationChanged(config);
177     }
178
179     private final DialogInterface.OnClickListener mAboutListener = new DialogInterface.OnClickListener() {
180         public void onClick(android.content.DialogInterface dialogInterface, int i) {
181             Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.zxing_url)));
182             startActivity(intent);
183         }
184     };
185
186     public void surfaceCreated(SurfaceHolder holder) {
187         CameraManager.get().openDriver(holder);
188         if (mHandler == null) {
189             mHandler = new BarcodesCaptureActivityHandler(this, mDecodeMode);
190         }
191     }
192
193     public void surfaceDestroyed(SurfaceHolder holder) {
194
195     }
196
197     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
198
199     }
200
201     /**
202      * A valid barcode has been found, so give an indication of success and show the results.
203      *
204      * @param rawResult The contents of the barcode.
205      * @param duration  How long the decoding took in milliseconds.
206      */
207     public void handleDecode(Result rawResult, int duration) {
208         if (!rawResult.toString().equals(mLastResult)) {
209             mLastResult = rawResult.toString();
210             playBeepSound();
211
212             ResultPoint[] points = rawResult.getResultPoints();
213             if (points != null && points.length > 0) {
214                 mViewfinderView.drawResultPoints(points);
215             }
216
217             TextView textView = (TextView) findViewById(R.id.status_text_view);
218             ParsedResult result = ResultHandler.parseResult(rawResult);
219             String displayResult = result.getDisplayResult();
220             displayResult = displayResult.replace("\r", "");
221             textView.setText(displayResult);
222
223             if (!mScanIntent) {
224                 Button actionButton = (Button) findViewById(R.id.status_action_button);
225                 int buttonText = ResultHandler.getActionButtonText(result.getType());
226                 if (buttonText != 0) {
227                     actionButton.setVisibility(View.VISIBLE);
228                     actionButton.setText(buttonText);
229                     ResultHandler resultHandler = new ResultHandler(this, result);
230                     actionButton.setOnClickListener(resultHandler);
231                     actionButton.requestFocus();
232                 } else {
233                     actionButton.setVisibility(View.GONE);
234                 }
235             }
236
237             View statusView = findViewById(R.id.status_view);
238             statusView.setBackgroundColor(getResources().getColor(R.color.result_points));
239
240             // Show the green finder patterns briefly, then either return the result or go back to
241             // continuous scanning.
242             if (mScanIntent) {
243                 Intent intent = new Intent(Intents.Scan.ACTION);
244                 intent.putExtra(Intents.Scan.RESULT,  rawResult.toString());
245                 intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
246                 Message message = Message.obtain(mHandler, R.id.return_scan_result);
247                 message.obj = intent;
248                 mHandler.sendMessageDelayed(message, 1000);
249             } else {
250                 Message message = Message.obtain(mHandler, R.id.restart_preview);
251                 mHandler.sendMessageDelayed(message, 2000);
252             }
253         } else if (mHandler != null) {
254             Message message = Message.obtain(mHandler, R.id.restart_preview);
255             message.sendToTarget();
256         }
257     }
258
259     /**
260      * Creates the beep MediaPlayer in advance so that the sound can be triggered with the least
261      * latency possible.
262      */
263     private void initBeepSound() {
264         if (mPlayBeep && mMediaPlayer == null) {
265             mMediaPlayer = new MediaPlayer();
266             mMediaPlayer.setAudioStreamType(AudioManager.STREAM_SYSTEM);
267             mMediaPlayer.setOnCompletionListener(mBeepListener);
268
269             AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.beep);
270             try {
271                 mMediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(),
272                         file.getLength());
273                 file.close();
274                 mMediaPlayer.setVolume(0.15f, 0.15f);
275                 mMediaPlayer.prepare();
276             } catch (IOException e) {
277                 mMediaPlayer = null;
278             }
279         }
280     }
281
282     private void playBeepSound() {
283         if (mPlayBeep && mMediaPlayer != null) {
284             mMediaPlayer.start();
285         }
286     }
287
288     /**
289      * When the beep has finished playing, rewind to queue up another one.
290      */
291     private final OnCompletionListener mBeepListener = new OnCompletionListener() {
292         public void onCompletion(MediaPlayer mediaPlayer) {
293             mediaPlayer.seekTo(0);
294         }
295     };
296
297     private void resetStatusView() {
298         resetStatusViewColor();
299         TextView textView = (TextView) findViewById(R.id.status_text_view);
300         textView.setText(R.string.msg_default_status);
301         Button actionButton = (Button) findViewById(R.id.status_action_button);
302         actionButton.setVisibility(View.GONE);
303         mLastResult = "";
304     }
305
306     public void resetStatusViewColor() {
307         View statusView = findViewById(R.id.status_view);
308         statusView.setBackgroundColor(getResources().getColor(R.color.status_view));
309     }
310
311     public void drawViewfinder() {
312         mViewfinderView.drawViewfinder();
313     }
314
315 }