Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright (C) 2008 ZXing authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.google.zxing.client.android;
     18 
     19 import com.google.zxing.BarcodeFormat;
     20 import com.google.zxing.Result;
     21 import com.google.zxing.ResultPoint;
     22 import com.google.zxing.client.android.camera.CameraManager;
     23 
     24 import android.app.Activity;
     25 import android.app.AlertDialog;
     26 import android.content.Intent;
     27 import android.graphics.Bitmap;
     28 import android.graphics.Canvas;
     29 import android.graphics.Paint;
     30 import android.graphics.Rect;
     31 import android.os.Bundle;
     32 import android.os.Handler;
     33 import android.util.Log;
     34 import android.view.KeyEvent;
     35 import android.view.SurfaceHolder;
     36 import android.view.SurfaceView;
     37 import android.view.View;
     38 import android.view.Window;
     39 import android.view.WindowManager;
     40 import android.widget.TextView;
     41 
     42 import java.io.IOException;
     43 
     44 /**
     45  * This activity opens the camera and does the actual scanning on a background
     46  * thread. It draws a viewfinder to help the user place the barcode correctly,
     47  * shows feedback as the image processing is happening, and then overlays the
     48  * results when a scan is successful.
     49  *
     50  * @author dswitkin (at) google.com (Daniel Switkin)
     51  * @author Sean Owen
     52  */
     53 public final class CaptureActivity extends Activity implements SurfaceHolder.Callback {
     54 
     55   private static final String LOG_TAG = CaptureActivity.class.getCanonicalName();
     56 
     57   private CameraManager cameraManager;
     58   private CaptureActivityHandler handler;
     59   private ViewfinderView viewfinderView;
     60   private TextView statusView;
     61   private boolean hasSurface;
     62   private String characterSet;
     63   private InactivityTimer inactivityTimer;
     64 
     65   ViewfinderView getViewfinderView() {
     66     return viewfinderView;
     67   }
     68 
     69   public Handler getHandler() {
     70     return handler;
     71   }
     72 
     73   CameraManager getCameraManager() {
     74     return cameraManager;
     75   }
     76 
     77   @Override
     78   public void onCreate(Bundle icicle) {
     79     super.onCreate(icicle);
     80 
     81     Window window = getWindow();
     82     window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
     83     setContentView(R.layout.capture);
     84 
     85     statusView = (TextView) findViewById(R.id.status_view);
     86     handler = null;
     87     hasSurface = false;
     88     inactivityTimer = new InactivityTimer(this);
     89   }
     90 
     91   @Override
     92   protected void onResume() {
     93     super.onResume();
     94 
     95     // CameraManager must be initialized here, not in onCreate(). This is
     96     // necessary because we don't want to open the camera driver and measure the
     97     // screen size if we're going to show the help on first launch. That led to
     98     // bugs where the scanning rectangle was the wrong size and partially off
     99     // screen.
    100     cameraManager = new CameraManager(getApplication());
    101     viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
    102     viewfinderView.setCameraManager(cameraManager);
    103 
    104     resetStatusView();
    105 
    106     SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
    107     SurfaceHolder surfaceHolder = surfaceView.getHolder();
    108     if (hasSurface) {
    109       // The activity was paused but not stopped, so the surface still exists.
    110       // Therefore
    111       // surfaceCreated() won't be called, so init the camera here.
    112       initCamera(surfaceHolder);
    113     } else {
    114       // Install the callback and wait for surfaceCreated() to init the camera.
    115       surfaceHolder.addCallback(this);
    116       surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    117     }
    118 
    119     Intent intent = getIntent();
    120     String action = intent == null ? null : intent.getAction();
    121     String dataString = intent == null ? null : intent.getDataString();
    122     if (intent != null && action != null) {
    123       if (action.equals(Intents.Scan.ACTION)) {
    124         if (intent.hasExtra(Intents.Scan.WIDTH) && intent.hasExtra(Intents.Scan.HEIGHT)) {
    125           int width = intent.getIntExtra(Intents.Scan.WIDTH, 0);
    126           int height = intent.getIntExtra(Intents.Scan.HEIGHT, 0);
    127           if (width > 0 && height > 0) {
    128             cameraManager.setManualFramingRect(width, height);
    129           }
    130         }
    131       }
    132       characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);
    133     } else {
    134       finish();
    135     }
    136 
    137     inactivityTimer.onResume();
    138   }
    139 
    140   @Override
    141   protected void onPause() {
    142     if (handler != null) {
    143       handler.quitSynchronously();
    144       handler = null;
    145     }
    146     inactivityTimer.onPause();
    147     cameraManager.closeDriver();
    148     if (!hasSurface) {
    149       SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
    150       SurfaceHolder surfaceHolder = surfaceView.getHolder();
    151       surfaceHolder.removeCallback(this);
    152     }
    153     super.onPause();
    154   }
    155 
    156   @Override
    157   protected void onDestroy() {
    158     inactivityTimer.shutdown();
    159     super.onDestroy();
    160   }
    161 
    162   @Override
    163   public boolean onKeyDown(int keyCode, KeyEvent event) {
    164     if (keyCode == KeyEvent.KEYCODE_BACK) {
    165       setResult(RESULT_CANCELED);
    166       finish();
    167       return true;
    168     } else if (keyCode == KeyEvent.KEYCODE_FOCUS || keyCode == KeyEvent.KEYCODE_CAMERA) {
    169       // Handle these events so they don't launch the Camera app
    170       return true;
    171     }
    172     return super.onKeyDown(keyCode, event);
    173   }
    174 
    175   public void surfaceCreated(SurfaceHolder holder) {
    176     if (holder == null) {
    177       Log.e(LOG_TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
    178     }
    179     if (!hasSurface) {
    180       hasSurface = true;
    181       initCamera(holder);
    182     }
    183   }
    184 
    185   public void surfaceDestroyed(SurfaceHolder holder) {
    186     hasSurface = false;
    187   }
    188 
    189   public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    190 
    191   }
    192 
    193   /**
    194    * A valid barcode has been found, so give an indication of success and show
    195    * the results.
    196    *
    197    * @param rawResult The contents of the barcode.
    198    * @param barcode A greyscale bitmap of the camera data which was decoded.
    199    */
    200   public void handleDecode(Result rawResult, Bitmap barcode) {
    201     inactivityTimer.onActivity();
    202 
    203     if (barcode == null) {
    204       Log.e(LOG_TAG, "Barcode not recognized");
    205       setResult(RESULT_CANCELED, null);
    206     } else {
    207       drawResultPoints(barcode, rawResult);
    208       Log.d(LOG_TAG, "Barcode is: " + rawResult.getText());
    209       Intent result = new Intent();
    210       result.putExtra("SCAN_RESULT", rawResult.getText());
    211       setResult(RESULT_OK, result);
    212     }
    213     finish();
    214   }
    215 
    216   /**
    217    * Superimpose a line for 1D or dots for 2D to highlight the key features of
    218    * the barcode.
    219    *
    220    * @param barcode A bitmap of the captured image.
    221    * @param rawResult The decoded results which contains the points to draw.
    222    */
    223   private void drawResultPoints(Bitmap barcode, Result rawResult) {
    224     ResultPoint[] points = rawResult.getResultPoints();
    225     if (points != null && points.length > 0) {
    226       Canvas canvas = new Canvas(barcode);
    227       Paint paint = new Paint();
    228       paint.setColor(getResources().getColor(R.color.result_image_border));
    229       paint.setStrokeWidth(3.0f);
    230       paint.setStyle(Paint.Style.STROKE);
    231       Rect border = new Rect(2, 2, barcode.getWidth() - 2, barcode.getHeight() - 2);
    232       canvas.drawRect(border, paint);
    233 
    234       paint.setColor(getResources().getColor(R.color.result_points));
    235       if (points.length == 2) {
    236         paint.setStrokeWidth(4.0f);
    237         drawLine(canvas, paint, points[0], points[1]);
    238       } else if (points.length == 4 && (rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A
    239           || rawResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
    240         // Hacky special case -- draw two lines, for the barcode and metadata
    241         drawLine(canvas, paint, points[0], points[1]);
    242         drawLine(canvas, paint, points[2], points[3]);
    243       } else {
    244         paint.setStrokeWidth(10.0f);
    245         for (ResultPoint point : points) {
    246           canvas.drawPoint(point.getX(), point.getY(), paint);
    247         }
    248       }
    249     }
    250   }
    251 
    252   private static void drawLine(Canvas canvas, Paint paint, ResultPoint a, ResultPoint b) {
    253     canvas.drawLine(a.getX(), a.getY(), b.getX(), b.getY(), paint);
    254   }
    255 
    256   private void initCamera(SurfaceHolder surfaceHolder) {
    257     try {
    258       cameraManager.openDriver(surfaceHolder);
    259       // Creating the handler starts the preview, which can also throw a
    260       // RuntimeException.
    261       if (handler == null) {
    262         handler = new CaptureActivityHandler(this, characterSet, cameraManager);
    263       }
    264     } catch (IOException ioe) {
    265       Log.w(LOG_TAG, ioe);
    266       displayFrameworkBugMessageAndExit();
    267     } catch (RuntimeException e) {
    268       // Barcode Scanner has seen crashes in the wild of this variety:
    269       // java.?lang.?RuntimeException: Fail to connect to camera service
    270       Log.w(LOG_TAG, "Unexpected error initializing camera", e);
    271       displayFrameworkBugMessageAndExit();
    272     }
    273   }
    274 
    275   private void displayFrameworkBugMessageAndExit() {
    276     AlertDialog.Builder builder = new AlertDialog.Builder(this);
    277     builder.setTitle("");
    278     builder.setMessage(getString(R.string.msg_camera_framework_bug));
    279     builder.setPositiveButton(android.R.string.ok, new FinishListener(this));
    280     builder.setOnCancelListener(new FinishListener(this));
    281     builder.show();
    282   }
    283 
    284   private void resetStatusView() {
    285     statusView.setText(R.string.msg_default_status);
    286     statusView.setVisibility(View.VISIBLE);
    287     viewfinderView.setVisibility(View.VISIBLE);
    288   }
    289 
    290   public void drawViewfinder() {
    291     viewfinderView.drawViewfinder();
    292   }
    293 }
    294