From 833ca52c276892e86878961320bbd8f425f6c8be Mon Sep 17 00:00:00 2001 From: dswitkin Date: Thu, 10 Sep 2009 22:18:58 +0000 Subject: [PATCH] Lots of updates: - Added support for new YUYV preview buffer format. - Made the preview rectangle larger. - Added support for high resolution screens. - Updated for Donut but still runs on Cupcake. - Converted to standard Java coding style for member variables. - Added many comments and author tags. - Removed some Petit Four hacks and vestigial code. *** The app must be built with the Donut SDK from now on. git-svn-id: https://zxing.googlecode.com/svn/trunk@1049 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- android/AndroidManifest.xml | 11 +- android/default.properties | 2 +- .../client/android/BaseLuminanceSource.java | 61 +++++ .../android/BookmarkPickerActivity.java | 18 +- .../zxing/client/android/CameraManager.java | 227 +++++++++++------- .../zxing/client/android/CaptureActivity.java | 208 ++++++++-------- .../android/CaptureActivityHandler.java | 51 ++-- .../google/zxing/client/android/Contents.java | 24 +- .../zxing/client/android/DecodeThread.java | 37 ++- .../zxing/client/android/EncodeActivity.java | 147 ++++++------ .../zxing/client/android/HelpActivity.java | 55 +++-- .../google/zxing/client/android/Intents.java | 17 +- .../InterleavedYUV422LuminanceSource.java | 138 +++++++++++ .../zxing/client/android/LocaleManager.java | 4 +- .../android/PlanarYUVLuminanceSource.java | 147 ++++++++++++ .../client/android/PreferencesActivity.java | 22 +- .../zxing/client/android/QRCodeEncoder.java | 132 +++++----- .../android/SearchBookContentsActivity.java | 171 ++++++------- .../android/SearchBookContentsAdapter.java | 7 +- .../android/SearchBookContentsListItem.java | 23 +- .../android/SearchBookContentsResult.java | 31 +-- .../zxing/client/android/ShareActivity.java | 70 +++--- .../zxing/client/android/ViewfinderView.java | 92 +++---- .../result/AddressBookResultHandler.java | 48 ++-- .../result/AndroidIntentParsedResult.java | 57 ----- .../android/result/CalendarResultHandler.java | 20 +- .../result/EmailAddressResultHandler.java | 18 +- .../android/result/GeoResultHandler.java | 18 +- .../android/result/ISBNResultHandler.java | 31 ++- .../android/result/ProductResultHandler.java | 30 ++- .../android/result/ResultButtonListener.java | 14 +- .../client/android/result/ResultHandler.java | 57 +++-- .../android/result/ResultHandlerFactory.java | 27 +-- .../android/result/SMSResultHandler.java | 22 +- .../android/result/TelResultHandler.java | 22 +- .../android/result/TextResultHandler.java | 21 +- .../android/result/URIResultHandler.java | 18 +- 37 files changed, 1249 insertions(+), 849 deletions(-) create mode 100644 android/src/com/google/zxing/client/android/BaseLuminanceSource.java create mode 100644 android/src/com/google/zxing/client/android/InterleavedYUV422LuminanceSource.java create mode 100644 android/src/com/google/zxing/client/android/PlanarYUVLuminanceSource.java delete mode 100755 android/src/com/google/zxing/client/android/result/AndroidIntentParsedResult.java diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 2275f3740..76e79aea9 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -20,9 +20,16 @@ version to be published. The next versionCode will be 7, regardless of whether t versionName is 2.31, 2.4, or 3.0. --> + android:versionName="3.0 alpha1" + android:versionCode="32"> + + + MAX_FRAME_WIDTH) { + width = MAX_FRAME_WIDTH; + } + int height = cameraResolution.y * 3 / 4; + if (height < MIN_FRAME_HEIGHT) { + height = MIN_FRAME_HEIGHT; + } else if (height > MAX_FRAME_HEIGHT) { + height = MAX_FRAME_HEIGHT; + } + int leftOffset = (cameraResolution.x - width) / 2; + int topOffset = (cameraResolution.y - height) / 2; + framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height); + Log.v(TAG, "Calculated framing rect: " + framingRect); } - return mFramingRect; + return framingRect; } /** @@ -171,31 +216,31 @@ final class CameraManager { } /** - * Preview frames are delivered here, which we pass on to the registered handler. Make sure to - * clear the handler so it will only receive one message. + * A factory method to build the appropriate LuminanceSource object based on the format + * of the preview buffers, as described by Camera.Parameters. + * + * @param data A preview frame. + * @param width The width of the image. + * @param height The height of the image. + * @return A BaseLuminanceSource subclass. */ - private final Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() { - public void onPreviewFrame(byte[] data, Camera camera) { - camera.setPreviewCallback(null); - if (mPreviewHandler != null) { - Message message = mPreviewHandler.obtainMessage(mPreviewMessage, mScreenResolution.x, - mScreenResolution.y, data); - message.sendToTarget(); - mPreviewHandler = null; - } + public BaseLuminanceSource buildLuminanceSource(byte[] data, int width, int height) { + Rect rect = getFramingRect(); + switch (previewFormat) { + case PixelFormat.YCbCr_420_SP: + case PixelFormat.YCbCr_422_SP: + return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, + rect.width(), rect.height()); + default: + // There's no PixelFormat constant for this buffer format yet. + if (previewFormatString.equals("yuv422i-yuyv")) { + return new InterleavedYUV422LuminanceSource(data, width, height, rect.left, rect.top, + rect.width(), rect.height()); + } + break; } - }; - - private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() { - public void onAutoFocus(boolean success, Camera camera) { - if (mAutoFocusHandler != null) { - Message message = mAutoFocusHandler.obtainMessage(mAutoFocusMessage, success); - // Simulate continuous autofocus by sending a focus request every 1.5 seconds. - mAutoFocusHandler.sendMessageDelayed(message, 1500L); - mAutoFocusHandler = null; - } - } - }; + return null; + } /** * Sets the camera up to take preview images which are used for both preview and decoding. We're @@ -203,13 +248,21 @@ final class CameraManager { * specify it explicitly with setPreviewFormat(). */ private void setCameraParameters() { - Camera.Parameters parameters = mCamera.getParameters(); + Camera.Parameters parameters = camera.getParameters(); Camera.Size size = parameters.getPreviewSize(); Log.v(TAG, "Default preview size: " + size.width + ", " + size.height); - Log.v(TAG, "Default preview format: " + parameters.getPreviewFormat()); - Log.v(TAG, "Setting preview size: " + mScreenResolution.x + ", " + mScreenResolution.y); + previewFormat = parameters.getPreviewFormat(); + previewFormatString = parameters.get("preview-format"); + Log.v(TAG, "Default preview format: " + previewFormat); - parameters.setPreviewSize(mScreenResolution.x, mScreenResolution.y); + // Ensure that the camera resolution is a multiple of 8, as the screen may not be. + // TODO: A better solution would be to request the supported preview resolutions + // and pick the best match, but this parameter is not standardized in Cupcake. + cameraResolution = new Point(); + cameraResolution.x = (screenResolution.x >> 3) << 3; + cameraResolution.y = (screenResolution.y >> 3) << 3; + Log.v(TAG, "Setting preview size: " + cameraResolution.x + ", " + cameraResolution.y); + parameters.setPreviewSize(cameraResolution.x, cameraResolution.y); // FIXME: This is a hack to turn the flash off on the Samsung Galaxy. parameters.set("flash-value", 2); @@ -217,16 +270,16 @@ final class CameraManager { // This is the standard setting to turn the flash off that all devices should honor. parameters.set("flash-mode", "off"); - mCamera.setParameters(parameters); + camera.setParameters(parameters); } private Point getScreenResolution() { - if (mScreenResolution == null) { - WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + if (screenResolution == null) { + WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); - mScreenResolution = new Point(display.getWidth(), display.getHeight()); + screenResolution = new Point(display.getWidth(), display.getHeight()); } - return mScreenResolution; + return screenResolution; } } diff --git a/android/src/com/google/zxing/client/android/CaptureActivity.java b/android/src/com/google/zxing/client/android/CaptureActivity.java index b2e4bf6f8..336ed4008 100755 --- a/android/src/com/google/zxing/client/android/CaptureActivity.java +++ b/android/src/com/google/zxing/client/android/CaptureActivity.java @@ -16,6 +16,12 @@ package com.google.zxing.client.android; +import com.google.zxing.Result; +import com.google.zxing.ResultPoint; +import com.google.zxing.client.android.result.ResultButtonListener; +import com.google.zxing.client.android.result.ResultHandler; +import com.google.zxing.client.android.result.ResultHandlerFactory; + import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; @@ -54,20 +60,16 @@ import android.view.WindowManager; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; -import com.google.zxing.Result; -import com.google.zxing.ResultPoint; -import com.google.zxing.client.android.result.ResultButtonListener; -import com.google.zxing.client.android.result.ResultHandler; -import com.google.zxing.client.android.result.ResultHandlerFactory; import java.io.IOException; /** * The barcode reader activity itself. This is loosely based on the CameraPreview * example included in the Android SDK. + * + * @author dswitkin@google.com (Daniel Switkin) */ public final class CaptureActivity extends Activity implements SurfaceHolder.Callback { - private static final String TAG = "CaptureActivity"; private static final int SHARE_ID = Menu.FIRST; @@ -92,23 +94,31 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal NONE } - public CaptureActivityHandler mHandler; + public CaptureActivityHandler handler; - private ViewfinderView mViewfinderView; - private View mStatusView; - private View mResultView; - private MediaPlayer mMediaPlayer; - private Result mLastResult; - private boolean mHasSurface; - private boolean mPlayBeep; - private boolean mVibrate; - private boolean mCopyToClipboard; - private Source mSource; - private String mSourceUrl; - private String mDecodeMode; - private String mVersionName; + private ViewfinderView viewfinderView; + private View statusView; + private View resultView; + private MediaPlayer mediaPlayer; + private Result lastResult; + private boolean hasSurface; + private boolean playBeep; + private boolean vibrate; + private boolean copyToClipboard; + private Source source; + private String sourceUrl; + private String decodeMode; + private String versionName; + + private final OnCompletionListener beepListener = new BeepListener(); - private final OnCompletionListener mBeepListener = new BeepListener(); + private final DialogInterface.OnClickListener aboutListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialogInterface, int i) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.zxing_url))); + startActivity(intent); + } + }; @Override public void onCreate(Bundle icicle) { @@ -120,12 +130,12 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal setContentView(R.layout.capture); CameraManager.init(getApplication()); - mViewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view); - mResultView = findViewById(R.id.result_view); - mStatusView = findViewById(R.id.status_view); - mHandler = null; - mLastResult = null; - mHasSurface = false; + viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view); + resultView = findViewById(R.id.result_view); + statusView = findViewById(R.id.status_view); + handler = null; + lastResult = null; + hasSurface = false; showHelpOnFirstLaunch(); } @@ -136,7 +146,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view); SurfaceHolder surfaceHolder = surfaceView.getHolder(); - if (mHasSurface) { + if (hasSurface) { // The activity was paused but not stopped, so the surface still exists. Therefore // surfaceCreated() won't be called, so init the camera here. initCamera(surfaceHolder); @@ -150,52 +160,52 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal String action = intent == null ? null : intent.getAction(); String dataString = intent == null ? null : intent.getDataString(); if (intent != null && action != null) { - if (action.equals(Intents.Scan.ACTION) || action.equals(Intents.Scan.DEPRECATED_ACTION)) { + if (action.equals(Intents.Scan.ACTION)) { // Scan the formats the intent requested, and return the result to the calling activity. - mSource = Source.NATIVE_APP_INTENT; - mDecodeMode = intent.getStringExtra(Intents.Scan.MODE); + source = Source.NATIVE_APP_INTENT; + decodeMode = intent.getStringExtra(Intents.Scan.MODE); resetStatusView(); } else if (dataString != null && dataString.contains(PRODUCT_SEARCH_URL_PREFIX) && dataString.contains(PRODUCT_SEARCH_URL_SUFFIX)) { // Scan only products and send the result to mobile Product Search. - mSource = Source.PRODUCT_SEARCH_LINK; - mSourceUrl = dataString; - mDecodeMode = Intents.Scan.PRODUCT_MODE; + source = Source.PRODUCT_SEARCH_LINK; + sourceUrl = dataString; + decodeMode = Intents.Scan.PRODUCT_MODE; resetStatusView(); } else if (dataString != null && dataString.equals(ZXING_URL)) { // Scan all formats and handle the results ourselves. // TODO: In the future we could allow the hyperlink to include a URL to send the results to. - mSource = Source.ZXING_LINK; - mSourceUrl = dataString; - mDecodeMode = null; + source = Source.ZXING_LINK; + sourceUrl = dataString; + decodeMode = null; resetStatusView(); } else { // Scan all formats and handle the results ourselves (launched from Home). - mSource = Source.NONE; - mDecodeMode = null; + source = Source.NONE; + decodeMode = null; resetStatusView(); } } else { - mSource = Source.NONE; - mDecodeMode = null; - if (mLastResult == null) { + source = Source.NONE; + decodeMode = null; + if (lastResult == null) { resetStatusView(); } } SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - mPlayBeep = prefs.getBoolean(PreferencesActivity.KEY_PLAY_BEEP, true); - mVibrate = prefs.getBoolean(PreferencesActivity.KEY_VIBRATE, false); - mCopyToClipboard = prefs.getBoolean(PreferencesActivity.KEY_COPY_TO_CLIPBOARD, true); + playBeep = prefs.getBoolean(PreferencesActivity.KEY_PLAY_BEEP, true); + vibrate = prefs.getBoolean(PreferencesActivity.KEY_VIBRATE, false); + copyToClipboard = prefs.getBoolean(PreferencesActivity.KEY_COPY_TO_CLIPBOARD, true); initBeepSound(); } @Override protected void onPause() { super.onPause(); - if (mHandler != null) { - mHandler.quitSynchronously(); - mHandler = null; + if (handler != null) { + handler.quitSynchronously(); + handler = null; } CameraManager.get().closeDriver(); } @@ -203,13 +213,13 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - if (mSource == Source.NATIVE_APP_INTENT) { + if (source == Source.NATIVE_APP_INTENT) { setResult(RESULT_CANCELED); finish(); return true; - } else if ((mSource == Source.NONE || mSource == Source.ZXING_LINK) && mLastResult != null) { + } else if ((source == Source.NONE || source == Source.ZXING_LINK) && lastResult != null) { resetStatusView(); - mHandler.sendEmptyMessage(R.id.restart_preview); + handler.sendEmptyMessage(R.id.restart_preview); return true; } } else if (keyCode == KeyEvent.KEYCODE_FOCUS || keyCode == KeyEvent.KEYCODE_CAMERA) { @@ -236,7 +246,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - menu.findItem(SHARE_ID).setVisible(mLastResult == null); + menu.findItem(SHARE_ID).setVisible(lastResult == null); return true; } @@ -263,10 +273,10 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal } case ABOUT_ID: AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(getString(R.string.title_about) + mVersionName); + builder.setTitle(getString(R.string.title_about) + versionName); builder.setMessage(getString(R.string.msg_about) + "\n\n" + getString(R.string.zxing_url)); builder.setIcon(R.drawable.zxing_icon); - builder.setPositiveButton(R.string.button_open_browser, mAboutListener); + builder.setPositiveButton(R.string.button_open_browser, aboutListener); builder.setNegativeButton(R.string.button_cancel, null); builder.show(); break; @@ -280,22 +290,15 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal super.onConfigurationChanged(config); } - private final DialogInterface.OnClickListener mAboutListener = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialogInterface, int i) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.zxing_url))); - startActivity(intent); - } - }; - public void surfaceCreated(SurfaceHolder holder) { - if (!mHasSurface) { - mHasSurface = true; + if (!hasSurface) { + hasSurface = true; initCamera(holder); } } public void surfaceDestroyed(SurfaceHolder holder) { - mHasSurface = false; + hasSurface = false; } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { @@ -309,11 +312,11 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal * @param barcode A greyscale bitmap of the camera data which was decoded. */ public void handleDecode(Result rawResult, Bitmap barcode) { - mLastResult = rawResult; + lastResult = rawResult; playBeepSoundAndVibrate(); drawResultPoints(barcode, rawResult); - switch (mSource) { + switch (source) { case NATIVE_APP_INTENT: case PRODUCT_SEARCH_LINK: handleDecodeExternally(rawResult, barcode); @@ -358,9 +361,9 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal // Put up our own UI for how to handle the decoded contents. private void handleDecodeInternally(Result rawResult, Bitmap barcode) { - mStatusView.setVisibility(View.GONE); - mViewfinderView.setVisibility(View.GONE); - mResultView.setVisibility(View.VISIBLE); + statusView.setVisibility(View.GONE); + viewfinderView.setVisibility(View.GONE); + resultView.setVisibility(View.VISIBLE); ImageView barcodeImageView = (ImageView) findViewById(R.id.barcode_image_view); barcodeImageView.setMaxWidth(MAX_RESULT_IMAGE_SIZE); @@ -398,7 +401,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal } } - if (mCopyToClipboard) { + if (copyToClipboard) { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); clipboard.setText(displayContents); } @@ -406,7 +409,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal // Briefly show the contents of the barcode, then handle the result outside Barcode Scanner. private void handleDecodeExternally(Result rawResult, Bitmap barcode) { - mViewfinderView.drawResultBitmap(barcode); + viewfinderView.drawResultBitmap(barcode); // Since this message will only be shown for a second, just tell the user what kind of // barcode was found (e.g. contact info) rather than the full contents, which they won't @@ -417,30 +420,30 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal textView.setTextSize(18.0f); textView.setText(getString(resultHandler.getDisplayTitle())); - mStatusView.setBackgroundColor(getResources().getColor(R.color.transparent)); + statusView.setBackgroundColor(getResources().getColor(R.color.transparent)); - if (mCopyToClipboard) { + if (copyToClipboard) { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); clipboard.setText(resultHandler.getDisplayContents()); } - if (mSource == Source.NATIVE_APP_INTENT) { + if (source == Source.NATIVE_APP_INTENT) { // Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when // the deprecated intent is retired. Intent intent = new Intent(getIntent().getAction()); intent.putExtra(Intents.Scan.RESULT, rawResult.toString()); intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString()); - Message message = Message.obtain(mHandler, R.id.return_scan_result); + Message message = Message.obtain(handler, R.id.return_scan_result); message.obj = intent; - mHandler.sendMessageDelayed(message, INTENT_RESULT_DURATION); - } else if (mSource == Source.PRODUCT_SEARCH_LINK) { + handler.sendMessageDelayed(message, INTENT_RESULT_DURATION); + } else if (source == Source.PRODUCT_SEARCH_LINK) { // Reformulate the URL which triggered us into a query, so that the request goes to the same // TLD as the scan URL. - Message message = Message.obtain(mHandler, R.id.launch_product_query); - int end = mSourceUrl.lastIndexOf("/scan"); - message.obj = mSourceUrl.substring(0, end) + "?q=" + + Message message = Message.obtain(handler, R.id.launch_product_query); + int end = sourceUrl.lastIndexOf("/scan"); + message.obj = sourceUrl.substring(0, end) + "?q=" + resultHandler.getDisplayContents().toString() + "&source=zxing"; - mHandler.sendMessageDelayed(message, INTENT_RESULT_DURATION); + handler.sendMessageDelayed(message, INTENT_RESULT_DURATION); } } @@ -455,7 +458,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal int currentVersion = info.versionCode; // Since we're paying to talk to the PackageManager anyway, it makes sense to cache the app // version name here for display in the about box later. - this.mVersionName = info.versionName; + this.versionName = info.versionName; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); int lastVersion = prefs.getInt(PreferencesActivity.KEY_HELP_VERSION_SHOWN, 0); if (currentVersion > lastVersion) { @@ -474,29 +477,29 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal * latency possible. */ private void initBeepSound() { - if (mPlayBeep && mMediaPlayer == null) { - mMediaPlayer = new MediaPlayer(); - mMediaPlayer.setAudioStreamType(AudioManager.STREAM_SYSTEM); - mMediaPlayer.setOnCompletionListener(mBeepListener); + if (playBeep && mediaPlayer == null) { + mediaPlayer = new MediaPlayer(); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_SYSTEM); + mediaPlayer.setOnCompletionListener(beepListener); AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.beep); try { - mMediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), + mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); file.close(); - mMediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME); - mMediaPlayer.prepare(); + mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME); + mediaPlayer.prepare(); } catch (IOException e) { - mMediaPlayer = null; + mediaPlayer = null; } } } private void playBeepSoundAndVibrate() { - if (mPlayBeep && mMediaPlayer != null) { - mMediaPlayer.start(); + if (playBeep && mediaPlayer != null) { + mediaPlayer.start(); } - if (mVibrate) { + if (vibrate) { Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); vibrator.vibrate(VIBRATE_DURATION); } @@ -509,27 +512,27 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal Log.w(TAG, ioe); return; } - if (mHandler == null) { - boolean beginScanning = mLastResult == null; - mHandler = new CaptureActivityHandler(this, mDecodeMode, beginScanning); + if (handler == null) { + boolean beginScanning = lastResult == null; + handler = new CaptureActivityHandler(this, decodeMode, beginScanning); } } private void resetStatusView() { - mResultView.setVisibility(View.GONE); - mStatusView.setVisibility(View.VISIBLE); - mStatusView.setBackgroundColor(getResources().getColor(R.color.status_view)); - mViewfinderView.setVisibility(View.VISIBLE); + resultView.setVisibility(View.GONE); + statusView.setVisibility(View.VISIBLE); + statusView.setBackgroundColor(getResources().getColor(R.color.status_view)); + viewfinderView.setVisibility(View.VISIBLE); TextView textView = (TextView) findViewById(R.id.status_text_view); textView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); textView.setTextSize(14.0f); textView.setText(R.string.msg_default_status); - mLastResult = null; + lastResult = null; } public void drawViewfinder() { - mViewfinderView.drawViewfinder(); + viewfinderView.drawViewfinder(); } /** @@ -540,5 +543,4 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal mediaPlayer.seekTo(0); } } - } diff --git a/android/src/com/google/zxing/client/android/CaptureActivityHandler.java b/android/src/com/google/zxing/client/android/CaptureActivityHandler.java index 0736842ec..1552f1691 100755 --- a/android/src/com/google/zxing/client/android/CaptureActivityHandler.java +++ b/android/src/com/google/zxing/client/android/CaptureActivityHandler.java @@ -16,6 +16,8 @@ package com.google.zxing.client.android; +import com.google.zxing.Result; + import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; @@ -23,16 +25,16 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; -import com.google.zxing.Result; /** * This class handles all the messaging which comprises the state machine for capture. + * + * @author dswitkin@google.com (Daniel Switkin) */ public final class CaptureActivityHandler extends Handler { - - private final CaptureActivity mActivity; - private final DecodeThread mDecodeThread; - private State mState; + private final CaptureActivity activity; + private final DecodeThread decodeThread; + private State state; private enum State { PREVIEW, @@ -42,10 +44,10 @@ public final class CaptureActivityHandler extends Handler { CaptureActivityHandler(CaptureActivity activity, String decodeMode, boolean beginScanning) { - mActivity = activity; - mDecodeThread = new DecodeThread(activity, decodeMode); - mDecodeThread.start(); - mState = State.SUCCESS; + this.activity = activity; + decodeThread = new DecodeThread(activity, decodeMode); + decodeThread.start(); + state = State.SUCCESS; // Start ourselves capturing previews and decoding. CameraManager.get().startPreview(); @@ -60,7 +62,7 @@ public final class CaptureActivityHandler extends Handler { case R.id.auto_focus: // When one auto focus pass finishes, start another. This is the closest thing to // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do. - if (mState == State.PREVIEW) { + if (state == State.PREVIEW) { CameraManager.get().requestAutoFocus(this, R.id.auto_focus); } break; @@ -68,34 +70,34 @@ public final class CaptureActivityHandler extends Handler { restartPreviewAndDecode(); break; case R.id.decode_succeeded: - mState = State.SUCCESS; + state = State.SUCCESS; Bundle bundle = message.getData(); Bitmap barcode = bundle.getParcelable(DecodeThread.BARCODE_BITMAP); - mActivity.handleDecode((Result) message.obj, barcode); + activity.handleDecode((Result) message.obj, barcode); break; case R.id.decode_failed: // We're decoding as fast as possible, so when one decode fails, start another. - mState = State.PREVIEW; - CameraManager.get().requestPreviewFrame(mDecodeThread.mHandler, R.id.decode); + state = State.PREVIEW; + CameraManager.get().requestPreviewFrame(decodeThread.handler, R.id.decode); break; case R.id.return_scan_result: - mActivity.setResult(Activity.RESULT_OK, (Intent) message.obj); - mActivity.finish(); + activity.setResult(Activity.RESULT_OK, (Intent) message.obj); + activity.finish(); break; case R.id.launch_product_query: String url = (String) message.obj; - mActivity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); break; } } public void quitSynchronously() { - mState = State.DONE; + state = State.DONE; CameraManager.get().stopPreview(); - Message quit = Message.obtain(mDecodeThread.mHandler, R.id.quit); + Message quit = Message.obtain(decodeThread.handler, R.id.quit); quit.sendToTarget(); try { - mDecodeThread.join(); + decodeThread.join(); } catch (InterruptedException e) { } @@ -105,12 +107,11 @@ public final class CaptureActivityHandler extends Handler { } private void restartPreviewAndDecode() { - if (mState == State.SUCCESS) { - mState = State.PREVIEW; - CameraManager.get().requestPreviewFrame(mDecodeThread.mHandler, R.id.decode); + if (state == State.SUCCESS) { + state = State.PREVIEW; + CameraManager.get().requestPreviewFrame(decodeThread.handler, R.id.decode); CameraManager.get().requestAutoFocus(this, R.id.auto_focus); - mActivity.drawViewfinder(); + activity.drawViewfinder(); } } - } diff --git a/android/src/com/google/zxing/client/android/Contents.java b/android/src/com/google/zxing/client/android/Contents.java index 04470aa82..ddcb6b05a 100755 --- a/android/src/com/google/zxing/client/android/Contents.java +++ b/android/src/com/google/zxing/client/android/Contents.java @@ -18,8 +18,13 @@ package com.google.zxing.client.android; import android.provider.Contacts; +/** + * The set of constants to use when sending Barcode Scanner an Intent which requests a barcode + * to be encoded. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class Contents { - private Contents() { } @@ -89,24 +94,19 @@ public final class Contents { } } - // These are new constants in Contacts.Intents.Insert for Android 1.1. - // TODO: Remove these constants once we can build against the 1.1 SDK. - private static final String SECONDARY_PHONE = "secondary_phone"; - private static final String TERTIARY_PHONE = "tertiary_phone"; - private static final String SECONDARY_EMAIL = "secondary_email"; - private static final String TERTIARY_EMAIL = "tertiary_email"; - - /** * When using Type.CONTACT, these arrays provide the keys for adding or retrieving multiple * phone numbers and addresses. */ public static final String[] PHONE_KEYS = { - Contacts.Intents.Insert.PHONE, SECONDARY_PHONE, TERTIARY_PHONE + Contacts.Intents.Insert.PHONE, + Contacts.Intents.Insert.SECONDARY_PHONE, + Contacts.Intents.Insert.TERTIARY_PHONE }; public static final String[] EMAIL_KEYS = { - Contacts.Intents.Insert.EMAIL, SECONDARY_EMAIL, TERTIARY_EMAIL + Contacts.Intents.Insert.EMAIL, + Contacts.Intents.Insert.SECONDARY_EMAIL, + Contacts.Intents.Insert.TERTIARY_EMAIL }; - } diff --git a/android/src/com/google/zxing/client/android/DecodeThread.java b/android/src/com/google/zxing/client/android/DecodeThread.java index bce87caff..bd2ade6d9 100755 --- a/android/src/com/google/zxing/client/android/DecodeThread.java +++ b/android/src/com/google/zxing/client/android/DecodeThread.java @@ -25,7 +25,6 @@ import com.google.zxing.Result; import com.google.zxing.common.GlobalHistogramBinarizer; import android.content.SharedPreferences; -import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -38,19 +37,20 @@ import java.util.Vector; /** * This thread does all the heavy lifting of decoding the images. + * + * @author dswitkin@google.com (Daniel Switkin) */ final class DecodeThread extends Thread { - public static final String BARCODE_BITMAP = "barcode_bitmap"; private static final String TAG = "DecodeThread"; - public Handler mHandler; - private final CaptureActivity mActivity; - private final MultiFormatReader mMultiFormatReader; + public Handler handler; + private final CaptureActivity activity; + private final MultiFormatReader multiFormatReader; DecodeThread(CaptureActivity activity, String mode) { - mActivity = activity; - mMultiFormatReader = new MultiFormatReader(); + this.activity = activity; + multiFormatReader = new MultiFormatReader(); // The prefs can't change while the thread is running, so pick them up once here. if (mode == null || mode.length() == 0) { @@ -80,7 +80,7 @@ final class DecodeThread extends Thread { @Override public void run() { Looper.prepare(); - mHandler = new Handler() { + handler = new Handler() { @Override public void handleMessage(Message message) { switch (message.what) { @@ -104,7 +104,7 @@ final class DecodeThread extends Thread { vector.addElement(BarcodeFormat.EAN_13); vector.addElement(BarcodeFormat.EAN_8); hints.put(DecodeHintType.POSSIBLE_FORMATS, vector); - mMultiFormatReader.setHints(hints); + multiFormatReader.setHints(hints); } /** @@ -121,7 +121,7 @@ final class DecodeThread extends Thread { vector.addElement(BarcodeFormat.CODE_128); vector.addElement(BarcodeFormat.ITF); hints.put(DecodeHintType.POSSIBLE_FORMATS, vector); - mMultiFormatReader.setHints(hints); + multiFormatReader.setHints(hints); } private void setDecodeQRMode() { @@ -129,7 +129,7 @@ final class DecodeThread extends Thread { Vector vector = new Vector(1); vector.addElement(BarcodeFormat.QR_CODE); hints.put(DecodeHintType.POSSIBLE_FORMATS, vector); - mMultiFormatReader.setHints(hints); + multiFormatReader.setHints(hints); } /** @@ -148,7 +148,7 @@ final class DecodeThread extends Thread { vector.addElement(BarcodeFormat.ITF); vector.addElement(BarcodeFormat.QR_CODE); hints.put(DecodeHintType.POSSIBLE_FORMATS, vector); - mMultiFormatReader.setHints(hints); + multiFormatReader.setHints(hints); } /** @@ -163,12 +163,10 @@ final class DecodeThread extends Thread { long start = System.currentTimeMillis(); boolean success; Result rawResult = null; - Rect rect = CameraManager.get().getFramingRect(); - YUVLuminanceSource source = new YUVLuminanceSource(data, width, height, rect.left, rect.top, - rect.width(), rect.height()); + BaseLuminanceSource source = CameraManager.get().buildLuminanceSource(data, width, height); BinaryBitmap bitmap = new BinaryBitmap(new GlobalHistogramBinarizer(source)); try { - rawResult = mMultiFormatReader.decodeWithState(bitmap); + rawResult = multiFormatReader.decodeWithState(bitmap); success = true; } catch (ReaderException e) { success = false; @@ -177,15 +175,14 @@ final class DecodeThread extends Thread { if (success) { Log.v(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString()); - Message message = Message.obtain(mActivity.mHandler, R.id.decode_succeeded, rawResult); + Message message = Message.obtain(activity.handler, R.id.decode_succeeded, rawResult); Bundle bundle = new Bundle(); - bundle.putParcelable(BARCODE_BITMAP, source.renderToBitmap()); + bundle.putParcelable(BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap()); message.setData(bundle); message.sendToTarget(); } else { - Message message = Message.obtain(mActivity.mHandler, R.id.decode_failed); + Message message = Message.obtain(activity.handler, R.id.decode_failed); message.sendToTarget(); } } - } diff --git a/android/src/com/google/zxing/client/android/EncodeActivity.java b/android/src/com/google/zxing/client/android/EncodeActivity.java index b77e354f5..0584697cb 100755 --- a/android/src/com/google/zxing/client/android/EncodeActivity.java +++ b/android/src/com/google/zxing/client/android/EncodeActivity.java @@ -35,20 +35,82 @@ import android.widget.TextView; /** * This class encodes data from an Intent into a QR code, and then displays it full screen so that * another person can scan it with their device. + * + * @author dswitkin@google.com (Daniel Switkin) */ public final class EncodeActivity extends Activity { + private QRCodeEncoder qrCodeEncoder; + private ProgressDialog progressDialog; + private boolean firstLayout; - private QRCodeEncoder mQRCodeEncoder; - private ProgressDialog mProgressDialog; - private boolean mFirstLayout; + /** + * This needs to be delayed until after the first layout so that the view dimensions will be + * available. + */ + public final OnGlobalLayoutListener layoutListener = new OnGlobalLayoutListener() { + public void onGlobalLayout() { + if (firstLayout) { + View layout = findViewById(R.id.encode_view); + int width = layout.getWidth(); + int height = layout.getHeight(); + int smallerDimension = width < height ? width : height; + smallerDimension = smallerDimension * 7 / 8; + + Intent intent = getIntent(); + try { + qrCodeEncoder = new QRCodeEncoder(EncodeActivity.this, intent); + setTitle(getString(R.string.app_name) + " - " + qrCodeEncoder.getTitle()); + qrCodeEncoder.requestBarcode(handler, smallerDimension); + progressDialog = ProgressDialog.show(EncodeActivity.this, null, + getString(R.string.msg_encode_in_progress), true, true, cancelListener); + } catch (IllegalArgumentException e) { + showErrorMessage(R.string.msg_encode_contents_failed); + } + firstLayout = false; + } + } + }; + + public final Handler handler = new Handler() { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case R.id.encode_succeeded: + progressDialog.dismiss(); + progressDialog = null; + Bitmap image = (Bitmap) message.obj; + ImageView view = (ImageView) findViewById(R.id.image_view); + view.setImageBitmap(image); + TextView contents = (TextView) findViewById(R.id.contents_text_view); + contents.setText(qrCodeEncoder.getDisplayContents()); + qrCodeEncoder = null; + break; + case R.id.encode_failed: + showErrorMessage(R.string.msg_encode_barcode_failed); + qrCodeEncoder = null; + break; + } + } + }; + + private final OnClickListener clickListener = new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }; + + private final OnCancelListener cancelListener = new OnCancelListener() { + public void onCancel(DialogInterface dialog) { + finish(); + } + }; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); Intent intent = getIntent(); - if (intent != null && (intent.getAction().equals(Intents.Encode.ACTION) || - intent.getAction().equals(Intents.Encode.DEPRECATED_ACTION))) { + if (intent != null && (intent.getAction().equals(Intents.Encode.ACTION))) { setContentView(R.layout.encode); } else { finish(); @@ -60,81 +122,18 @@ public final class EncodeActivity extends Activity { super.onResume(); View layout = findViewById(R.id.encode_view); - layout.getViewTreeObserver().addOnGlobalLayoutListener(mLayoutListener); - mFirstLayout = true; + layout.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener); + firstLayout = true; } - /** - * This needs to be delayed until after the first layout so that the view dimensions will be - * available. - */ - public final OnGlobalLayoutListener mLayoutListener = new OnGlobalLayoutListener() { - public void onGlobalLayout() { - if (mFirstLayout) { - View layout = findViewById(R.id.encode_view); - int width = layout.getWidth(); - int height = layout.getHeight(); - int smallerDimension = width < height ? width : height; - smallerDimension = smallerDimension * 7 / 8; - - Intent intent = getIntent(); - try { - mQRCodeEncoder = new QRCodeEncoder(EncodeActivity.this, intent); - setTitle(getString(R.string.app_name) + " - " + mQRCodeEncoder.getTitle()); - mQRCodeEncoder.requestBarcode(mHandler, smallerDimension); - mProgressDialog = ProgressDialog.show(EncodeActivity.this, null, - getString(R.string.msg_encode_in_progress), true, true, mCancelListener); - } catch (IllegalArgumentException e) { - showErrorMessage(R.string.msg_encode_contents_failed); - } - mFirstLayout = false; - } - } - }; - - public final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message message) { - switch (message.what) { - case R.id.encode_succeeded: - mProgressDialog.dismiss(); - mProgressDialog = null; - Bitmap image = (Bitmap) message.obj; - ImageView view = (ImageView) findViewById(R.id.image_view); - view.setImageBitmap(image); - TextView contents = (TextView) findViewById(R.id.contents_text_view); - contents.setText(mQRCodeEncoder.getDisplayContents()); - mQRCodeEncoder = null; - break; - case R.id.encode_failed: - showErrorMessage(R.string.msg_encode_barcode_failed); - mQRCodeEncoder = null; - break; - } - } - }; - private void showErrorMessage(int message) { - if (mProgressDialog != null) { - mProgressDialog.dismiss(); - mProgressDialog = null; + if (progressDialog != null) { + progressDialog.dismiss(); + progressDialog = null; } AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(message); - builder.setPositiveButton(R.string.button_ok, mClickListener); + builder.setPositiveButton(R.string.button_ok, clickListener); builder.show(); } - - private final OnClickListener mClickListener = new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }; - - private final OnCancelListener mCancelListener = new OnCancelListener() { - public void onCancel(DialogInterface dialog) { - finish(); - } - }; - } diff --git a/android/src/com/google/zxing/client/android/HelpActivity.java b/android/src/com/google/zxing/client/android/HelpActivity.java index 175066f0d..49530df8a 100644 --- a/android/src/com/google/zxing/client/android/HelpActivity.java +++ b/android/src/com/google/zxing/client/android/HelpActivity.java @@ -25,33 +25,46 @@ import android.webkit.WebViewClient; import android.widget.Button; /** + * An HTML-based help screen with Back and Done buttons at the bottom. + * * @author dswitkin@google.com (Daniel Switkin) */ public final class HelpActivity extends Activity { - private static final String DEFAULT_URL = "file:///android_asset/html/index.html"; - private WebView mWebView; - private Button mBackButton; + private WebView webView; + private Button backButton; + + private final Button.OnClickListener backListener = new Button.OnClickListener() { + public void onClick(View view) { + webView.goBack(); + } + }; + + private final Button.OnClickListener doneListener = new Button.OnClickListener() { + public void onClick(View view) { + finish(); + } + }; @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.help); - mWebView = (WebView)findViewById(R.id.help_contents); - mWebView.setWebViewClient(new HelpClient()); + webView = (WebView)findViewById(R.id.help_contents); + webView.setWebViewClient(new HelpClient()); if (icicle != null) { - mWebView.restoreState(icicle); + webView.restoreState(icicle); } else { - mWebView.loadUrl(DEFAULT_URL); + webView.loadUrl(DEFAULT_URL); } - mBackButton = (Button)findViewById(R.id.back_button); - mBackButton.setOnClickListener(mBackListener); + backButton = (Button)findViewById(R.id.back_button); + backButton.setOnClickListener(backListener); Button doneButton = (Button)findViewById(R.id.done_button); - doneButton.setOnClickListener(mDoneListener); + doneButton.setOnClickListener(doneListener); } @Override @@ -61,40 +74,26 @@ public final class HelpActivity extends Activity { @Override protected void onSaveInstanceState(Bundle state) { - mWebView.saveState(state); + webView.saveState(state); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - if (mWebView.canGoBack()) { - mWebView.goBack(); + if (webView.canGoBack()) { + webView.goBack(); return true; } } return super.onKeyDown(keyCode, event); } - private final Button.OnClickListener mBackListener = new Button.OnClickListener() { - public void onClick(View view) { - mWebView.goBack(); - } - }; - - private final Button.OnClickListener mDoneListener = new Button.OnClickListener() { - public void onClick(View view) { - finish(); - } - }; - private final class HelpClient extends WebViewClient { - @Override public void onPageFinished(WebView view, String url) { setTitle(view.getTitle()); - mBackButton.setEnabled(view.canGoBack()); + backButton.setEnabled(view.canGoBack()); } - } } diff --git a/android/src/com/google/zxing/client/android/Intents.java b/android/src/com/google/zxing/client/android/Intents.java index 5853e3392..bea5306ec 100755 --- a/android/src/com/google/zxing/client/android/Intents.java +++ b/android/src/com/google/zxing/client/android/Intents.java @@ -16,8 +16,13 @@ package com.google.zxing.client.android; +/** + * This class provides the constants to use when sending an Intent to Barcode Scanner. + * These strings are effectively API and cannot be changed. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class Intents { - private Intents() { } @@ -28,9 +33,6 @@ public final class Intents { */ public static final String ACTION = "com.google.zxing.client.android.SCAN"; - // For compatibility only - do not use in new code, this will go away! - public static final String DEPRECATED_ACTION = "com.android.barcodes.SCAN"; - /** * By default, sending Scan.ACTION will decode all barcodes that we understand. However it * may be useful to limit scanning to certain formats. Use Intent.putExtra(MODE, value) with @@ -79,9 +81,6 @@ public final class Intents { */ public static final String ACTION = "com.google.zxing.client.android.ENCODE"; - // For compatibility only - do not use in new code, this will go away! - public static final String DEPRECATED_ACTION = "com.android.barcodes.ENCODE"; - /** * The data to encode. Use Intent.putExtra(DATA, data) where data is either a String or a * Bundle, depending on the type and format specified. Non-QR Code formats should @@ -112,9 +111,6 @@ public final class Intents { */ public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS"; - // For compatibility only - do not use in new code, this will go away! - public static final String DEPRECATED_ACTION = "com.android.barcodes.SEARCH_BOOK_CONTENTS"; - /** * The book to search, identified by ISBN number. */ @@ -139,5 +135,4 @@ public final class Intents { private Share() { } } - } diff --git a/android/src/com/google/zxing/client/android/InterleavedYUV422LuminanceSource.java b/android/src/com/google/zxing/client/android/InterleavedYUV422LuminanceSource.java new file mode 100644 index 000000000..16c749522 --- /dev/null +++ b/android/src/com/google/zxing/client/android/InterleavedYUV422LuminanceSource.java @@ -0,0 +1,138 @@ +/* + * Copyright 2009 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.client.android; + +import com.google.zxing.LuminanceSource; + +import android.graphics.Bitmap; + +/** + * This object extends LuminanceSource around an array of YUV data returned from the camera driver, + * with the option to crop to a rectangle within the full data. This can be used to exclude + * superfluous pixels around the perimeter and speed up decoding. + * + * It handles YUV 422 interleaved data, where each pixel consists of first a Y value, then + * a color value, with U and V alternating at each pixel. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class InterleavedYUV422LuminanceSource extends BaseLuminanceSource { + private final byte[] yuvData; + private final int dataWidth; + private final int dataHeight; + private final int left; + private final int top; + + public InterleavedYUV422LuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, + int left, int top, int width, int height) { + super(width, height); + + if (left + width > dataWidth || top + height > dataHeight) { + throw new IllegalArgumentException("Crop rectangle does not fit within image data."); + } + + this.yuvData = yuvData; + this.dataWidth = dataWidth; + this.dataHeight = dataHeight; + this.left = left; + this.top = top; + } + + @Override + public byte[] getRow(int y, byte[] row) { + if (y < 0 || y >= getHeight()) { + throw new IllegalArgumentException("Requested row is outside the image: " + y); + } + int width = getWidth(); + if (row == null || row.length < width) { + row = new byte[width]; + } + int offset = (y + top) * dataWidth * 2 + (left * 2); + byte[] yuv = yuvData; + for (int x = 0; x < width; x++) { + row[x] = yuv[offset + (x << 1)]; + } + return row; + } + + @Override + public byte[] getMatrix() { + int width = getWidth(); + int height = getHeight(); + int area = width * height; + byte[] matrix = new byte[area]; + int inputOffset = top * dataWidth * 2 + (left * 2); + byte[] yuv = yuvData; + + for (int y = 0; y < height; y++) { + int outputOffset = y * width; + for (int x = 0; x < width; x++) { + matrix[outputOffset + x] = yuv[inputOffset + (x << 1)]; + } + inputOffset += (dataWidth * 2); + } + return matrix; + } + + @Override + public boolean isCropSupported() { + return true; + } + + @Override + public LuminanceSource crop(int left, int top, int width, int height) { + return new InterleavedYUV422LuminanceSource(yuvData, dataWidth, dataHeight, left, top, + width, height); + } + + @Override + public int getDataWidth() { + return dataWidth; + } + + @Override + public int getDataHeight() { + return dataHeight; + } + + @Override + public Bitmap renderCroppedGreyscaleBitmap() { + int width = getWidth(); + int height = getHeight(); + int[] pixels = new int[width * height]; + byte[] yuv = yuvData; + int inputOffset = top * dataWidth * 2 + (left * 2); + + for (int y = 0; y < height; y++) { + int outputOffset = y * width; + for (int x = 0; x < width; x++) { + int grey = yuv[inputOffset + (x << 1)] & 0xff; + pixels[outputOffset + x] = (0xff000000) | (grey * 0x00010101); + } + inputOffset += (dataWidth * 2); + } + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return bitmap; + } + + @Override + public Bitmap renderFullColorBitmap(boolean halfSize) { + return null; + } +} diff --git a/android/src/com/google/zxing/client/android/LocaleManager.java b/android/src/com/google/zxing/client/android/LocaleManager.java index 917c135c5..ece817452 100644 --- a/android/src/com/google/zxing/client/android/LocaleManager.java +++ b/android/src/com/google/zxing/client/android/LocaleManager.java @@ -22,9 +22,10 @@ import java.util.HashMap; /** * Handles any locale-specific logic for the client. + * + * @author Sean Owen */ public final class LocaleManager { - private static final String DEFAULT_TLD = "com"; private static final Map GOOGLE_COUNTRY_TLD; static { @@ -93,5 +94,4 @@ public final class LocaleManager { } return tld; } - } diff --git a/android/src/com/google/zxing/client/android/PlanarYUVLuminanceSource.java b/android/src/com/google/zxing/client/android/PlanarYUVLuminanceSource.java new file mode 100644 index 000000000..ac8e043ea --- /dev/null +++ b/android/src/com/google/zxing/client/android/PlanarYUVLuminanceSource.java @@ -0,0 +1,147 @@ +/* + * Copyright 2009 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.client.android; + +import com.google.zxing.LuminanceSource; + +import android.graphics.Bitmap; + +/** + * This object extends LuminanceSource around an array of YUV data returned from the camera driver, + * with the option to crop to a rectangle within the full data. This can be used to exclude + * superfluous pixels around the perimeter and speed up decoding. + * + * It works for any pixel format where the Y channel is planar and appears first, including + * YCbCr_420_SP and YCbCr_422_SP. Any subsequent color data will be ignored. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class PlanarYUVLuminanceSource extends BaseLuminanceSource { + private final byte[] yuvData; + private final int dataWidth; + private final int dataHeight; + private final int left; + private final int top; + + public PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top, + int width, int height) { + super(width, height); + + if (left + width > dataWidth || top + height > dataHeight) { + throw new IllegalArgumentException("Crop rectangle does not fit within image data."); + } + + this.yuvData = yuvData; + this.dataWidth = dataWidth; + this.dataHeight = dataHeight; + this.left = left; + this.top = top; + } + + @Override + public byte[] getRow(int y, byte[] row) { + if (y < 0 || y >= getHeight()) { + throw new IllegalArgumentException("Requested row is outside the image: " + y); + } + int width = getWidth(); + if (row == null || row.length < width) { + row = new byte[width]; + } + int offset = (y + top) * dataWidth + left; + System.arraycopy(yuvData, offset, row, 0, width); + return row; + } + + @Override + public byte[] getMatrix() { + int width = getWidth(); + int height = getHeight(); + + // If the caller asks for the entire underlying image, save the copy and give them the + // original data. The docs specifically warn that result.length must be ignored. + if (width == dataWidth && height == dataHeight) { + return yuvData; + } + + int area = width * height; + byte[] matrix = new byte[area]; + int inputOffset = top * dataWidth + left; + + // If the width matches the full width of the underlying data, perform a single copy. + if (width == dataWidth) { + System.arraycopy(yuvData, inputOffset, matrix, 0, area); + return matrix; + } + + // Otherwise copy one cropped row at a time. + byte[] yuv = yuvData; + for (int y = 0; y < height; y++) { + int outputOffset = y * width; + System.arraycopy(yuv, inputOffset, matrix, outputOffset, width); + inputOffset += dataWidth; + } + return matrix; + } + + @Override + public boolean isCropSupported() { + return true; + } + + @Override + public LuminanceSource crop(int left, int top, int width, int height) { + return new PlanarYUVLuminanceSource(yuvData, dataWidth, dataHeight, left, top, width, height); + } + + @Override + public int getDataWidth() { + return dataWidth; + } + + @Override + public int getDataHeight() { + return dataHeight; + } + + @Override + public Bitmap renderCroppedGreyscaleBitmap() { + int width = getWidth(); + int height = getHeight(); + int[] pixels = new int[width * height]; + byte[] yuv = yuvData; + int inputOffset = top * dataWidth + left; + + for (int y = 0; y < height; y++) { + int outputOffset = y * width; + for (int x = 0; x < width; x++) { + int grey = yuv[inputOffset + x] & 0xff; + pixels[outputOffset + x] = (0xff000000) | (grey * 0x00010101); + } + inputOffset += dataWidth; + } + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return bitmap; + } + + // Can't be implemented here, as the color representations vary. + @Override + public Bitmap renderFullColorBitmap(boolean halfSize) { + return null; + } +} diff --git a/android/src/com/google/zxing/client/android/PreferencesActivity.java b/android/src/com/google/zxing/client/android/PreferencesActivity.java index 847f4fb72..e15233769 100755 --- a/android/src/com/google/zxing/client/android/PreferencesActivity.java +++ b/android/src/com/google/zxing/client/android/PreferencesActivity.java @@ -23,6 +23,11 @@ import android.preference.CheckBoxPreference; import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; +/** + * The main settings activity. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class PreferencesActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener { @@ -36,8 +41,8 @@ public final class PreferencesActivity extends PreferenceActivity static final String KEY_HELP_VERSION_SHOWN = "preferences_help_version_shown"; - CheckBoxPreference mDecode1D; - CheckBoxPreference mDecodeQR; + CheckBoxPreference decode1D; + CheckBoxPreference decodeQR; @Override protected void onCreate(Bundle icicle) { @@ -46,19 +51,18 @@ public final class PreferencesActivity extends PreferenceActivity PreferenceScreen preferences = getPreferenceScreen(); preferences.getSharedPreferences().registerOnSharedPreferenceChangeListener(this); - mDecode1D = (CheckBoxPreference) preferences.findPreference(KEY_DECODE_1D); - mDecodeQR = (CheckBoxPreference) preferences.findPreference(KEY_DECODE_QR); + decode1D = (CheckBoxPreference) preferences.findPreference(KEY_DECODE_1D); + decodeQR = (CheckBoxPreference) preferences.findPreference(KEY_DECODE_QR); } // Prevent the user from turning off both decode options public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(KEY_DECODE_1D)) { - mDecodeQR.setEnabled(mDecode1D.isChecked()); - mDecodeQR.setChecked(true); + decodeQR.setEnabled(decode1D.isChecked()); + decodeQR.setChecked(true); } else if (key.equals(KEY_DECODE_QR)) { - mDecode1D.setEnabled(mDecodeQR.isChecked()); - mDecode1D.setChecked(true); + decode1D.setEnabled(decodeQR.isChecked()); + decode1D.setChecked(true); } } - } diff --git a/android/src/com/google/zxing/client/android/QRCodeEncoder.java b/android/src/com/google/zxing/client/android/QRCodeEncoder.java index a66155250..4a58a5891 100755 --- a/android/src/com/google/zxing/client/android/QRCodeEncoder.java +++ b/android/src/com/google/zxing/client/android/QRCodeEncoder.java @@ -16,6 +16,11 @@ package com.google.zxing.client.android; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.ByteMatrix; + import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; @@ -23,48 +28,49 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.Contacts; -import android.util.Log; import android.telephony.PhoneNumberUtils; -import com.google.zxing.BarcodeFormat; -import com.google.zxing.MultiFormatWriter; -import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import android.util.Log; +/** + * This class does the work of decoding the user's request and extracting all the data + * to be encoded in a QR Code. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class QRCodeEncoder { - - private final Activity mActivity; - private String mContents; - private String mDisplayContents; - private String mTitle; - private BarcodeFormat mFormat; + private final Activity activity; + private String contents; + private String displayContents; + private String title; + private BarcodeFormat format; public QRCodeEncoder(Activity activity, Intent intent) { - mActivity = activity; + this.activity = activity; if (!encodeContents(intent)) { throw new IllegalArgumentException("No valid data to encode."); } } public void requestBarcode(Handler handler, int pixelResolution) { - Thread encodeThread = new EncodeThread(mContents, handler, pixelResolution, - mFormat); + Thread encodeThread = new EncodeThread(contents, handler, pixelResolution, + format); encodeThread.start(); } public String getContents() { - return mContents; + return contents; } public String getDisplayContents() { - return mDisplayContents; + return displayContents; } public String getTitle() { - return mTitle; + return title; } public String getFormat() { - return mFormat.toString(); + return format.toString(); } // It would be nice if the string encoding lived in the core ZXing library, @@ -82,59 +88,59 @@ public final class QRCodeEncoder { if (type == null || type.length() == 0) { return false; } - mFormat = BarcodeFormat.QR_CODE; + this.format = BarcodeFormat.QR_CODE; encodeQRCodeContents(intent, type); } else { String data = intent.getStringExtra(Intents.Encode.DATA); if (data != null && data.length() != 0) { - mContents = data; - mDisplayContents = data; - mTitle = mActivity.getString(R.string.contents_text); + contents = data; + displayContents = data; + title = activity.getString(R.string.contents_text); if (format.equals(Contents.Format.CODE_128)) - mFormat = BarcodeFormat.CODE_128; + this.format = BarcodeFormat.CODE_128; else if (format.equals(Contents.Format.CODE_39)) - mFormat = BarcodeFormat.CODE_39; + this.format = BarcodeFormat.CODE_39; else if (format.equals(Contents.Format.EAN_8)) - mFormat = BarcodeFormat.EAN_8; + this.format = BarcodeFormat.EAN_8; else if (format.equals(Contents.Format.EAN_13)) - mFormat = BarcodeFormat.EAN_13; + this.format = BarcodeFormat.EAN_13; else if (format.equals(Contents.Format.UPC_A)) - mFormat = BarcodeFormat.UPC_A; + this.format = BarcodeFormat.UPC_A; else if (format.equals(Contents.Format.UPC_E)) - mFormat = BarcodeFormat.UPC_E; + this.format = BarcodeFormat.UPC_E; } } - return mContents != null && mContents.length() > 0; + return contents != null && contents.length() > 0; } private void encodeQRCodeContents(Intent intent, String type) { if (type.equals(Contents.Type.TEXT)) { String data = intent.getStringExtra(Intents.Encode.DATA); if (data != null && data.length() > 0) { - mContents = data; - mDisplayContents = data; - mTitle = mActivity.getString(R.string.contents_text); + contents = data; + displayContents = data; + title = activity.getString(R.string.contents_text); } } else if (type.equals(Contents.Type.EMAIL)) { String data = intent.getStringExtra(Intents.Encode.DATA); if (data != null && data.length() > 0) { - mContents = "mailto:" + data; - mDisplayContents = data; - mTitle = mActivity.getString(R.string.contents_email); + contents = "mailto:" + data; + displayContents = data; + title = activity.getString(R.string.contents_email); } } else if (type.equals(Contents.Type.PHONE)) { String data = intent.getStringExtra(Intents.Encode.DATA); if (data != null && data.length() > 0) { - mContents = "tel:" + data; - mDisplayContents = PhoneNumberUtils.formatNumber(data); - mTitle = mActivity.getString(R.string.contents_phone); + contents = "tel:" + data; + displayContents = PhoneNumberUtils.formatNumber(data); + title = activity.getString(R.string.contents_phone); } } else if (type.equals(Contents.Type.SMS)) { String data = intent.getStringExtra(Intents.Encode.DATA); if (data != null && data.length() > 0) { - mContents = "sms:" + data; - mDisplayContents = PhoneNumberUtils.formatNumber(data); - mTitle = mActivity.getString(R.string.contents_sms); + contents = "sms:" + data; + displayContents = PhoneNumberUtils.formatNumber(data); + title = activity.getString(R.string.contents_sms); } } else if (type.equals(Contents.Type.CONTACT)) { Bundle bundle = intent.getBundleExtra(Intents.Encode.DATA); @@ -169,12 +175,12 @@ public final class QRCodeEncoder { // Make sure we've encoded at least one field. if (newDisplayContents.length() > 0) { newContents.append(';'); - mContents = newContents.toString(); - mDisplayContents = newDisplayContents.toString(); - mTitle = mActivity.getString(R.string.contents_contact); + contents = newContents.toString(); + displayContents = newDisplayContents.toString(); + title = activity.getString(R.string.contents_contact); } else { - mContents = null; - mDisplayContents = null; + contents = null; + displayContents = null; } } } else if (type.equals(Contents.Type.LOCATION)) { @@ -184,36 +190,35 @@ public final class QRCodeEncoder { float latitude = bundle.getFloat("LAT", Float.MAX_VALUE); float longitude = bundle.getFloat("LONG", Float.MAX_VALUE); if (latitude != Float.MAX_VALUE && longitude != Float.MAX_VALUE) { - mContents = "geo:" + latitude + ',' + longitude; - mDisplayContents = latitude + "," + longitude; - mTitle = mActivity.getString(R.string.contents_location); + contents = "geo:" + latitude + ',' + longitude; + displayContents = latitude + "," + longitude; + title = activity.getString(R.string.contents_location); } } } } private static final class EncodeThread extends Thread { - private static final String TAG = "EncodeThread"; - private final String mContents; - private final Handler mHandler; - private final int mPixelResolution; - private final BarcodeFormat mFormat; + private final String contents; + private final Handler handler; + private final int pixelResolution; + private final BarcodeFormat format; EncodeThread(String contents, Handler handler, int pixelResolution, BarcodeFormat format) { - mContents = contents; - mHandler = handler; - mPixelResolution = pixelResolution; - mFormat = format; + this.contents = contents; + this.handler = handler; + this.pixelResolution = pixelResolution; + this.format = format; } @Override public void run() { try { - ByteMatrix result = new MultiFormatWriter().encode(mContents, - mFormat, mPixelResolution, mPixelResolution); + ByteMatrix result = new MultiFormatWriter().encode(contents, format, + pixelResolution, pixelResolution); int width = result.getWidth(); int height = result.getHeight(); byte[][] array = result.getArray(); @@ -228,19 +233,18 @@ public final class QRCodeEncoder { Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); - Message message = Message.obtain(mHandler, R.id.encode_succeeded); + Message message = Message.obtain(handler, R.id.encode_succeeded); message.obj = bitmap; message.sendToTarget(); } catch (WriterException e) { Log.e(TAG, e.toString()); - Message message = Message.obtain(mHandler, R.id.encode_failed); + Message message = Message.obtain(handler, R.id.encode_failed); message.sendToTarget(); } catch (IllegalArgumentException e) { Log.e(TAG, e.toString()); - Message message = Message.obtain(mHandler, R.id.encode_failed); + Message message = Message.obtain(handler, R.id.encode_failed); message.sendToTarget(); } } } - } diff --git a/android/src/com/google/zxing/client/android/SearchBookContentsActivity.java b/android/src/com/google/zxing/client/android/SearchBookContentsActivity.java index 9ebe61e31..b84c73d46 100644 --- a/android/src/com/google/zxing/client/android/SearchBookContentsActivity.java +++ b/android/src/com/google/zxing/client/android/SearchBookContentsActivity.java @@ -48,17 +48,53 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; +/** + * Uses Google Book Search to find a word or phrase in the requested book. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class SearchBookContentsActivity extends Activity { - private static final String TAG = "SearchBookContents"; private static final String USER_AGENT = "ZXing (Android)"; - private NetworkThread mNetworkThread; - private String mISBN; - private EditText mQueryTextView; - private Button mQueryButton; - private ListView mResultListView; - private TextView mHeaderView; + private NetworkThread networkThread; + private String isbn; + private EditText queryTextView; + private Button queryButton; + private ListView resultListView; + private TextView headerView; + + public final Handler handler = new Handler() { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case R.id.search_book_contents_succeeded: + handleSearchResults((JSONObject) message.obj); + resetForNewQuery(); + break; + case R.id.search_book_contents_failed: + resetForNewQuery(); + headerView.setText(R.string.msg_sbc_failed); + break; + } + } + }; + + private final Button.OnClickListener buttonListener = new Button.OnClickListener() { + public void onClick(View view) { + launchSearch(); + } + }; + + private final View.OnKeyListener keyListener = new View.OnKeyListener() { + public boolean onKey(View view, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_ENTER) { + launchSearch(); + return true; + } + return false; + } + }; @Override public void onCreate(Bundle icicle) { @@ -69,39 +105,38 @@ public final class SearchBookContentsActivity extends Activity { CookieManager.getInstance().removeExpiredCookie(); Intent intent = getIntent(); - if (intent == null || (!intent.getAction().equals(Intents.SearchBookContents.ACTION) && - !intent.getAction().equals(Intents.SearchBookContents.DEPRECATED_ACTION))) { + if (intent == null || (!intent.getAction().equals(Intents.SearchBookContents.ACTION))) { finish(); return; } - mISBN = intent.getStringExtra(Intents.SearchBookContents.ISBN); - setTitle(getString(R.string.sbc_name) + ": ISBN " + mISBN); + isbn = intent.getStringExtra(Intents.SearchBookContents.ISBN); + setTitle(getString(R.string.sbc_name) + ": ISBN " + isbn); setContentView(R.layout.search_book_contents); - mQueryTextView = (EditText) findViewById(R.id.query_text_view); + queryTextView = (EditText) findViewById(R.id.query_text_view); String initialQuery = intent.getStringExtra(Intents.SearchBookContents.QUERY); if (initialQuery != null && initialQuery.length() > 0) { // Populate the search box but don't trigger the search - mQueryTextView.setText(initialQuery); + queryTextView.setText(initialQuery); } - mQueryTextView.setOnKeyListener(mKeyListener); + queryTextView.setOnKeyListener(keyListener); - mQueryButton = (Button) findViewById(R.id.query_button); - mQueryButton.setOnClickListener(mButtonListener); + queryButton = (Button) findViewById(R.id.query_button); + queryButton.setOnClickListener(buttonListener); - mResultListView = (ListView) findViewById(R.id.result_list_view); + resultListView = (ListView) findViewById(R.id.result_list_view); LayoutInflater factory = LayoutInflater.from(this); - mHeaderView = (TextView) factory.inflate(R.layout.search_book_contents_header, - mResultListView, false); - mResultListView.addHeaderView(mHeaderView); + headerView = (TextView) factory.inflate(R.layout.search_book_contents_header, + resultListView, false); + resultListView.addHeaderView(headerView); } @Override protected void onResume() { super.onResume(); - mQueryTextView.selectAll(); + queryTextView.selectAll(); } @Override @@ -110,55 +145,23 @@ public final class SearchBookContentsActivity extends Activity { super.onConfigurationChanged(config); } - public final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message message) { - switch (message.what) { - case R.id.search_book_contents_succeeded: - handleSearchResults((JSONObject) message.obj); - resetForNewQuery(); - break; - case R.id.search_book_contents_failed: - resetForNewQuery(); - mHeaderView.setText(R.string.msg_sbc_failed); - break; - } - } - }; - private void resetForNewQuery() { - mNetworkThread = null; - mQueryTextView.setEnabled(true); - mQueryTextView.selectAll(); - mQueryButton.setEnabled(true); + networkThread = null; + queryTextView.setEnabled(true); + queryTextView.selectAll(); + queryButton.setEnabled(true); } - private final Button.OnClickListener mButtonListener = new Button.OnClickListener() { - public void onClick(View view) { - launchSearch(); - } - }; - - private final View.OnKeyListener mKeyListener = new View.OnKeyListener() { - public boolean onKey(View view, int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_ENTER) { - launchSearch(); - return true; - } - return false; - } - }; - private void launchSearch() { - if (mNetworkThread == null) { - String query = mQueryTextView.getText().toString(); + if (networkThread == null) { + String query = queryTextView.getText().toString(); if (query != null && query.length() > 0) { - mNetworkThread = new NetworkThread(mISBN, query, mHandler); - mNetworkThread.start(); - mHeaderView.setText(R.string.msg_sbc_searching_book); - mResultListView.setAdapter(null); - mQueryTextView.setEnabled(false); - mQueryButton.setEnabled(false); + networkThread = new NetworkThread(isbn, query, handler); + networkThread.start(); + headerView.setText(R.string.msg_sbc_searching_book); + resultListView.setAdapter(null); + queryTextView.setEnabled(false); + queryButton.setEnabled(false); } } } @@ -168,26 +171,26 @@ public final class SearchBookContentsActivity extends Activity { private void handleSearchResults(JSONObject json) { try { int count = json.getInt("number_of_results"); - mHeaderView.setText("Found " + (count == 1 ? "1 result" : count + " results")); + headerView.setText("Found " + (count == 1 ? "1 result" : count + " results")); if (count > 0) { JSONArray results = json.getJSONArray("search_results"); - SearchBookContentsResult.setQuery(mQueryTextView.getText().toString()); + SearchBookContentsResult.setQuery(queryTextView.getText().toString()); List items = new ArrayList(count); for (int x = 0; x < count; x++) { items.add(parseResult(results.getJSONObject(x))); } - mResultListView.setAdapter(new SearchBookContentsAdapter(this, items)); + resultListView.setAdapter(new SearchBookContentsAdapter(this, items)); } else { String searchable = json.optString("searchable"); if ("false".equals(searchable)) { - mHeaderView.setText(R.string.msg_sbc_book_not_searchable); + headerView.setText(R.string.msg_sbc_book_not_searchable); } - mResultListView.setAdapter(null); + resultListView.setAdapter(null); } } catch (JSONException e) { Log.e(TAG, e.toString()); - mResultListView.setAdapter(null); - mHeaderView.setText(R.string.msg_sbc_failed); + resultListView.setAdapter(null); + headerView.setText(R.string.msg_sbc_failed); } } @@ -223,15 +226,14 @@ public final class SearchBookContentsActivity extends Activity { } private static final class NetworkThread extends Thread { - - private final String mISBN; - private final String mQuery; - private final Handler mHandler; + private final String isbn; + private final String query; + private final Handler handler; NetworkThread(String isbn, String query, Handler handler) { - mISBN = isbn; - mQuery = query; - mHandler = handler; + this.isbn = isbn; + this.query = query; + this.handler = handler; } @Override @@ -241,8 +243,8 @@ public final class SearchBookContentsActivity extends Activity { // These return a JSON result which describes if and where the query was found. This API may // break or disappear at any time in the future. Since this is an API call rather than a // website, we don't use LocaleManager to change the TLD. - URI uri = new URI("http", null, "www.google.com", -1, "/books", "vid=isbn" + mISBN + - "&jscmd=SearchWithinVolume2&q=" + mQuery, null); + URI uri = new URI("http", null, "www.google.com", -1, "/books", "vid=isbn" + isbn + + "&jscmd=SearchWithinVolume2&q=" + query, null); HttpUriRequest get = new HttpGet(uri); get.setHeader("cookie", getCookie(uri.toString())); client = AndroidHttpClient.newInstance(USER_AGENT); @@ -255,17 +257,17 @@ public final class SearchBookContentsActivity extends Activity { JSONObject json = new JSONObject(jsonHolder.toString(getEncoding(entity))); jsonHolder.close(); - Message message = Message.obtain(mHandler, R.id.search_book_contents_succeeded); + Message message = Message.obtain(handler, R.id.search_book_contents_succeeded); message.obj = json; message.sendToTarget(); } else { Log.e(TAG, "HTTP returned " + response.getStatusLine().getStatusCode() + " for " + uri); - Message message = Message.obtain(mHandler, R.id.search_book_contents_failed); + Message message = Message.obtain(handler, R.id.search_book_contents_failed); message.sendToTarget(); } } catch (Exception e) { Log.e(TAG, e.toString()); - Message message = Message.obtain(mHandler, R.id.search_book_contents_failed); + Message message = Message.obtain(handler, R.id.search_book_contents_failed); message.sendToTarget(); } finally { if (client != null) { @@ -316,5 +318,4 @@ public final class SearchBookContentsActivity extends Activity { // return "UTF-8"; } } - } diff --git a/android/src/com/google/zxing/client/android/SearchBookContentsAdapter.java b/android/src/com/google/zxing/client/android/SearchBookContentsAdapter.java index 5b32cf5ed..4f9e4c3f8 100644 --- a/android/src/com/google/zxing/client/android/SearchBookContentsAdapter.java +++ b/android/src/com/google/zxing/client/android/SearchBookContentsAdapter.java @@ -24,8 +24,12 @@ import android.widget.ArrayAdapter; import java.util.List; +/** + * Manufactures list items which represent SBC results. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class SearchBookContentsAdapter extends ArrayAdapter { - public SearchBookContentsAdapter(Context context, List items) { super(context, R.layout.search_book_contents_list_item, 0, items); } @@ -50,5 +54,4 @@ public final class SearchBookContentsAdapter extends ArrayAdapter 0) { if (result.getValidSnippet()) { @@ -64,14 +68,13 @@ public final class SearchBookContentsListItem extends LinearLayout { styledSnippet.setSpan(boldSpan, pos, pos + queryLength, 0); offset = pos + queryLength; } - mSnippetView.setText(styledSnippet); + snippetView.setText(styledSnippet); } else { // This may be an error message, so don't try to bold the query terms within it - mSnippetView.setText(snippet); + snippetView.setText(snippet); } } else { - mSnippetView.setText(""); + snippetView.setText(""); } } - } diff --git a/android/src/com/google/zxing/client/android/SearchBookContentsResult.java b/android/src/com/google/zxing/client/android/SearchBookContentsResult.java index 0363d6ec9..de561a768 100644 --- a/android/src/com/google/zxing/client/android/SearchBookContentsResult.java +++ b/android/src/com/google/zxing/client/android/SearchBookContentsResult.java @@ -16,38 +16,41 @@ package com.google.zxing.client.android; +/** + * The underlying data for a SBC result. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class SearchBookContentsResult { + private static String query; - private static String sQuery; - - private final String mPageNumber; - private final String mSnippet; - private final boolean mValidSnippet; + private final String pageNumber; + private final String snippet; + private final boolean validSnippet; public SearchBookContentsResult(String pageNumber, String snippet, boolean validSnippet) { - mPageNumber = pageNumber; - mSnippet = snippet; - mValidSnippet = validSnippet; + this.pageNumber = pageNumber; + this.snippet = snippet; + this.validSnippet = validSnippet; } public static void setQuery(String query) { - sQuery = query; + SearchBookContentsResult.query = query; } public String getPageNumber() { - return mPageNumber; + return pageNumber; } public String getSnippet() { - return mSnippet; + return snippet; } public boolean getValidSnippet() { - return mValidSnippet; + return validSnippet; } public static String getQuery() { - return sQuery; + return query; } - } diff --git a/android/src/com/google/zxing/client/android/ShareActivity.java b/android/src/com/google/zxing/client/android/ShareActivity.java index 34a54dcee..db302dd71 100755 --- a/android/src/com/google/zxing/client/android/ShareActivity.java +++ b/android/src/com/google/zxing/client/android/ShareActivity.java @@ -29,8 +29,13 @@ import android.text.ClipboardManager; import android.view.View; import android.widget.Button; +/** + * Barcode Scanner can share data like contacts and bookmarks by displaying a QR Code on screen, + * such that another user can scan the barcode with their phone. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class ShareActivity extends Activity { - private static final int PICK_BOOKMARK = 0; private static final int PICK_CONTACT = 1; @@ -51,43 +56,16 @@ public final class ShareActivity extends Activity { Contacts.PhonesColumns.NUMBER // 1 }; - private Button mClipboardButton; + private Button clipboardButton; - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - setContentView(R.layout.share); - - Button mContactButton = (Button) findViewById(R.id.contact_button); - mContactButton.setOnClickListener(mContactListener); - Button mBookmarkButton = (Button) findViewById(R.id.bookmark_button); - mBookmarkButton.setOnClickListener(mBookmarkListener); - mClipboardButton = (Button) findViewById(R.id.clipboard_button); - mClipboardButton.setOnClickListener(mClipboardListener); - } - - @Override - protected void onResume() { - super.onResume(); - - ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - if (clipboard.hasText()) { - mClipboardButton.setEnabled(true); - mClipboardButton.setText(R.string.button_share_clipboard); - } else { - mClipboardButton.setEnabled(false); - mClipboardButton.setText(R.string.button_clipboard_empty); - } - } - - private final Button.OnClickListener mContactListener = new Button.OnClickListener() { + private final Button.OnClickListener contactListener = new Button.OnClickListener() { public void onClick(View v) { startActivityForResult(new Intent(Intent.ACTION_PICK, Contacts.People.CONTENT_URI), PICK_CONTACT); } }; - private final Button.OnClickListener mBookmarkListener = new Button.OnClickListener() { + private final Button.OnClickListener bookmarkListener = new Button.OnClickListener() { public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_PICK); intent.setClassName(ShareActivity.this, BookmarkPickerActivity.class.getName()); @@ -95,7 +73,7 @@ public final class ShareActivity extends Activity { } }; - private final Button.OnClickListener mClipboardListener = new Button.OnClickListener() { + private final Button.OnClickListener clipboardListener = new Button.OnClickListener() { public void onClick(View v) { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); // Should always be true, because we grey out the clipboard button in onResume() if it's empty @@ -109,6 +87,33 @@ public final class ShareActivity extends Activity { } }; + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.share); + + Button mContactButton = (Button) findViewById(R.id.contact_button); + mContactButton.setOnClickListener(contactListener); + Button mBookmarkButton = (Button) findViewById(R.id.bookmark_button); + mBookmarkButton.setOnClickListener(bookmarkListener); + clipboardButton = (Button) findViewById(R.id.clipboard_button); + clipboardButton.setOnClickListener(clipboardListener); + } + + @Override + protected void onResume() { + super.onResume(); + + ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + if (clipboard.hasText()) { + clipboardButton.setEnabled(true); + clipboardButton.setText(R.string.button_share_clipboard); + } else { + clipboardButton.setEnabled(false); + clipboardButton.setText(R.string.button_clipboard_empty); + } + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { if (resultCode == RESULT_OK) { @@ -213,5 +218,4 @@ public final class ShareActivity extends Activity { } return data; } - } diff --git a/android/src/com/google/zxing/client/android/ViewfinderView.java b/android/src/com/google/zxing/client/android/ViewfinderView.java index 47d82af2f..0937056e2 100755 --- a/android/src/com/google/zxing/client/android/ViewfinderView.java +++ b/android/src/com/google/zxing/client/android/ViewfinderView.java @@ -28,34 +28,35 @@ import android.view.View; /** * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial * transparency outside it, as well as the laser scanner animation and result points. + * + * @author dswitkin@google.com (Daniel Switkin) */ public final class ViewfinderView extends View { - private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64}; private static final long ANIMATION_DELAY = 100L; - private final Paint mPaint; - private final Rect mBox; - private Bitmap mResultBitmap; - private final int mMaskColor; - private final int mResultColor; - private final int mFrameColor; - private final int mLaserColor; - private int mScannerAlpha; + private final Paint paint; + private final Rect box; + private Bitmap resultBitmap; + private final int maskColor; + private final int resultColor; + private final int frameColor; + private final int laserColor; + private int scannerAlpha; // This constructor is used when the class is built from an XML resource. public ViewfinderView(Context context, AttributeSet attrs) { super(context, attrs); // Initialize these once for performance rather than calling them every time in onDraw(). - mPaint = new Paint(); - mBox = new Rect(); + paint = new Paint(); + box = new Rect(); Resources resources = getResources(); - mMaskColor = resources.getColor(R.color.viewfinder_mask); - mResultColor = resources.getColor(R.color.result_view); - mFrameColor = resources.getColor(R.color.viewfinder_frame); - mLaserColor = resources.getColor(R.color.viewfinder_laser); - mScannerAlpha = 0; + maskColor = resources.getColor(R.color.viewfinder_mask); + resultColor = resources.getColor(R.color.result_view); + frameColor = resources.getColor(R.color.viewfinder_frame); + laserColor = resources.getColor(R.color.viewfinder_laser); + scannerAlpha = 0; } @Override @@ -65,48 +66,48 @@ public final class ViewfinderView extends View { int height = canvas.getHeight(); // Draw the exterior (i.e. outside the framing rect) darkened - mPaint.setColor(mResultBitmap != null ? mResultColor : mMaskColor); - mBox.set(0, 0, width, frame.top); - canvas.drawRect(mBox, mPaint); - mBox.set(0, frame.top, frame.left, frame.bottom + 1); - canvas.drawRect(mBox, mPaint); - mBox.set(frame.right + 1, frame.top, width, frame.bottom + 1); - canvas.drawRect(mBox, mPaint); - mBox.set(0, frame.bottom + 1, width, height); - canvas.drawRect(mBox, mPaint); + paint.setColor(resultBitmap != null ? resultColor : maskColor); + box.set(0, 0, width, frame.top); + canvas.drawRect(box, paint); + box.set(0, frame.top, frame.left, frame.bottom + 1); + canvas.drawRect(box, paint); + box.set(frame.right + 1, frame.top, width, frame.bottom + 1); + canvas.drawRect(box, paint); + box.set(0, frame.bottom + 1, width, height); + canvas.drawRect(box, paint); - if (mResultBitmap != null) { + if (resultBitmap != null) { // Draw the opaque result bitmap over the scanning rectangle - mPaint.setAlpha(255); - canvas.drawBitmap(mResultBitmap, frame.left, frame.top, mPaint); + paint.setAlpha(255); + canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint); } else { // Draw a two pixel solid black border inside the framing rect - mPaint.setColor(mFrameColor); - mBox.set(frame.left, frame.top, frame.right + 1, frame.top + 2); - canvas.drawRect(mBox, mPaint); - mBox.set(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1); - canvas.drawRect(mBox, mPaint); - mBox.set(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1); - canvas.drawRect(mBox, mPaint); - mBox.set(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1); - canvas.drawRect(mBox, mPaint); + paint.setColor(frameColor); + box.set(frame.left, frame.top, frame.right + 1, frame.top + 2); + canvas.drawRect(box, paint); + box.set(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1); + canvas.drawRect(box, paint); + box.set(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1); + canvas.drawRect(box, paint); + box.set(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1); + canvas.drawRect(box, paint); // Draw a red "laser scanner" line through the middle to show decoding is active - mPaint.setColor(mLaserColor); - mPaint.setAlpha(SCANNER_ALPHA[mScannerAlpha]); - mScannerAlpha = (mScannerAlpha + 1) % SCANNER_ALPHA.length; + paint.setColor(laserColor); + paint.setAlpha(SCANNER_ALPHA[scannerAlpha]); + scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length; int middle = frame.height() / 2 + frame.top; - mBox.set(frame.left + 2, middle - 1, frame.right - 1, middle + 2); - canvas.drawRect(mBox, mPaint); + box.set(frame.left + 2, middle - 1, frame.right - 1, middle + 2); + canvas.drawRect(box, paint); // Request another update at the animation interval, but only repaint the laser line, // not the entire viewfinder mask. - postInvalidateDelayed(ANIMATION_DELAY, mBox.left, mBox.top, mBox.right, mBox.bottom); + postInvalidateDelayed(ANIMATION_DELAY, box.left, box.top, box.right, box.bottom); } } public void drawViewfinder() { - mResultBitmap = null; + resultBitmap = null; invalidate(); } @@ -116,8 +117,7 @@ public final class ViewfinderView extends View { * @param barcode An image of the decoded barcode. */ public void drawResultBitmap(Bitmap barcode) { - mResultBitmap = barcode; + resultBitmap = barcode; invalidate(); } - } diff --git a/android/src/com/google/zxing/client/android/result/AddressBookResultHandler.java b/android/src/com/google/zxing/client/android/result/AddressBookResultHandler.java index 3fb83321e..59228add0 100644 --- a/android/src/com/google/zxing/client/android/result/AddressBookResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/AddressBookResultHandler.java @@ -16,34 +16,39 @@ package com.google.zxing.client.android.result; -import android.app.Activity; -import android.telephony.PhoneNumberUtils; -import android.text.SpannableString; -import android.text.Spannable; -import android.text.style.StyleSpan; import com.google.zxing.client.android.R; import com.google.zxing.client.result.AddressBookParsedResult; import com.google.zxing.client.result.ParsedResult; +import android.app.Activity; +import android.telephony.PhoneNumberUtils; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.StyleSpan; + import java.text.DateFormat; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; +/** + * Handles address book entries. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class AddressBookResultHandler extends ResultHandler { - private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); - private final boolean[] mFields; - private int mButtonCount; + private final boolean[] fields; + private int buttonCount; // This takes all the work out of figuring out which buttons/actions should be in which // positions, based on which fields are present in this barcode. private int mapIndexToAction(int index) { - if (index < mButtonCount) { + if (index < buttonCount) { int count = -1; for (int x = 0; x < MAX_BUTTON_COUNT; x++) { - if (mFields[x]) { + if (fields[x]) { count++; } if (count == index) { @@ -64,23 +69,23 @@ public final class AddressBookResultHandler extends ResultHandler { String[] emails = addressResult.getEmails(); boolean hasEmailAddress = emails != null && emails.length > 0; - mFields = new boolean[MAX_BUTTON_COUNT]; - mFields[0] = true; // Add contact is always available - mFields[1] = hasAddress; - mFields[2] = hasPhoneNumber; - mFields[3] = hasEmailAddress; + fields = new boolean[MAX_BUTTON_COUNT]; + fields[0] = true; // Add contact is always available + fields[1] = hasAddress; + fields[2] = hasPhoneNumber; + fields[3] = hasEmailAddress; - mButtonCount = 0; + buttonCount = 0; for (int x = 0; x < MAX_BUTTON_COUNT; x++) { - if (mFields[x]) { - mButtonCount++; + if (fields[x]) { + buttonCount++; } } } @Override public int getButtonCount() { - return mButtonCount; + return buttonCount; } @Override @@ -102,7 +107,7 @@ public final class AddressBookResultHandler extends ResultHandler { @Override public void handleButtonPress(int index) { - AddressBookParsedResult addressResult = (AddressBookParsedResult) mResult; + AddressBookParsedResult addressResult = (AddressBookParsedResult) result; int action = mapIndexToAction(index); switch (action) { case 0: @@ -130,7 +135,7 @@ public final class AddressBookResultHandler extends ResultHandler { // Overriden so we can hyphenate phone numbers, format birthdays, and bold the name. @Override public CharSequence getDisplayContents() { - AddressBookParsedResult result = (AddressBookParsedResult) mResult; + AddressBookParsedResult result = (AddressBookParsedResult) this.result; StringBuffer contents = new StringBuffer(); ParsedResult.maybeAppend(result.getNames(), contents); int namesLength = contents.length(); @@ -178,5 +183,4 @@ public final class AddressBookResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_address_book; } - } diff --git a/android/src/com/google/zxing/client/android/result/AndroidIntentParsedResult.java b/android/src/com/google/zxing/client/android/result/AndroidIntentParsedResult.java deleted file mode 100755 index 063ed57f6..000000000 --- a/android/src/com/google/zxing/client/android/result/AndroidIntentParsedResult.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.client.android.result; - -import android.content.Intent; -import com.google.zxing.client.result.ParsedResult; -import com.google.zxing.client.result.ParsedResultType; - -import java.net.URISyntaxException; - -/** - * A {@link com.google.zxing.client.result.ParsedResult} derived from a URI that encodes an Android - * {@link Intent}, and which should presumably trigger that intent on Android. - */ -public final class AndroidIntentParsedResult extends ParsedResult { - - private final Intent mIntent; - - private AndroidIntentParsedResult(Intent intent) { - super(ParsedResultType.ANDROID_INTENT); - mIntent = intent; - } - - public static AndroidIntentParsedResult parse(String rawText) { - try { - return new AndroidIntentParsedResult(Intent.getIntent(rawText)); - } catch (URISyntaxException urise) { - return null; - } catch (IllegalArgumentException iae) { - return null; - } - } - - public Intent getIntent() { - return mIntent; - } - - @Override - public String getDisplayResult() { - return mIntent.toString(); - } - -} diff --git a/android/src/com/google/zxing/client/android/result/CalendarResultHandler.java b/android/src/com/google/zxing/client/android/result/CalendarResultHandler.java index a3dab605c..c58500b6f 100644 --- a/android/src/com/google/zxing/client/android/result/CalendarResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/CalendarResultHandler.java @@ -16,11 +16,12 @@ package com.google.zxing.client.android.result; -import android.app.Activity; import com.google.zxing.client.android.R; import com.google.zxing.client.result.CalendarParsedResult; import com.google.zxing.client.result.ParsedResult; +import android.app.Activity; + import java.text.DateFormat; import java.text.ParsePosition; import java.text.SimpleDateFormat; @@ -28,12 +29,16 @@ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +/** + * Handles calendar entries encoded in QR Codes. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public final class CalendarResultHandler extends ResultHandler { - private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); private static final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); - private static final int[] mButtons = { + private static final int[] buttons = { R.string.button_add_calendar }; @@ -43,17 +48,17 @@ public final class CalendarResultHandler extends ResultHandler { @Override public int getButtonCount() { - return mButtons.length; + return buttons.length; } @Override public int getButtonText(int index) { - return mButtons[index]; + return buttons[index]; } @Override public void handleButtonPress(int index) { - CalendarParsedResult calendarResult = (CalendarParsedResult) mResult; + CalendarParsedResult calendarResult = (CalendarParsedResult) result; switch (index) { case 0: addCalendarEvent(calendarResult.getSummary(), calendarResult.getStart(), @@ -64,7 +69,7 @@ public final class CalendarResultHandler extends ResultHandler { @Override public CharSequence getDisplayContents() { - CalendarParsedResult calResult = (CalendarParsedResult) mResult; + CalendarParsedResult calResult = (CalendarParsedResult) result; StringBuffer result = new StringBuffer(); ParsedResult.maybeAppend(calResult.getSummary(), result); appendTime(calResult.getStart(), result); @@ -110,5 +115,4 @@ public final class CalendarResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_calendar; } - } diff --git a/android/src/com/google/zxing/client/android/result/EmailAddressResultHandler.java b/android/src/com/google/zxing/client/android/result/EmailAddressResultHandler.java index 72808600b..35c4fe58e 100644 --- a/android/src/com/google/zxing/client/android/result/EmailAddressResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/EmailAddressResultHandler.java @@ -16,14 +16,19 @@ package com.google.zxing.client.android.result; -import android.app.Activity; import com.google.zxing.client.android.R; import com.google.zxing.client.result.EmailAddressParsedResult; import com.google.zxing.client.result.ParsedResult; -public final class EmailAddressResultHandler extends ResultHandler { +import android.app.Activity; - private static final int[] mButtons = { +/** + * Handles email addresses. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class EmailAddressResultHandler extends ResultHandler { + private static final int[] buttons = { R.string.button_email, R.string.button_add_contact }; @@ -34,17 +39,17 @@ public final class EmailAddressResultHandler extends ResultHandler { @Override public int getButtonCount() { - return mButtons.length; + return buttons.length; } @Override public int getButtonText(int index) { - return mButtons[index]; + return buttons[index]; } @Override public void handleButtonPress(int index) { - EmailAddressParsedResult emailResult = (EmailAddressParsedResult) mResult; + EmailAddressParsedResult emailResult = (EmailAddressParsedResult) result; switch (index) { case 0: sendEmailFromUri(emailResult.getMailtoURI(), null, null); @@ -61,5 +66,4 @@ public final class EmailAddressResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_email_address; } - } diff --git a/android/src/com/google/zxing/client/android/result/GeoResultHandler.java b/android/src/com/google/zxing/client/android/result/GeoResultHandler.java index 071e29668..a569ef136 100644 --- a/android/src/com/google/zxing/client/android/result/GeoResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/GeoResultHandler.java @@ -16,14 +16,19 @@ package com.google.zxing.client.android.result; -import android.app.Activity; import com.google.zxing.client.android.R; import com.google.zxing.client.result.GeoParsedResult; import com.google.zxing.client.result.ParsedResult; -public final class GeoResultHandler extends ResultHandler { +import android.app.Activity; - private static final int[] mButtons = { +/** + * Handles geographic coordinates (typically encoded as geo: URLs). + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class GeoResultHandler extends ResultHandler { + private static final int[] buttons = { R.string.button_show_map, R.string.button_get_directions }; @@ -34,17 +39,17 @@ public final class GeoResultHandler extends ResultHandler { @Override public int getButtonCount() { - return mButtons.length; + return buttons.length; } @Override public int getButtonText(int index) { - return mButtons[index]; + return buttons[index]; } @Override public void handleButtonPress(int index) { - GeoParsedResult geoResult = (GeoParsedResult) mResult; + GeoParsedResult geoResult = (GeoParsedResult) result; switch (index) { case 0: openMap(geoResult.getGeoURI()); @@ -59,5 +64,4 @@ public final class GeoResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_geo; } - } diff --git a/android/src/com/google/zxing/client/android/result/ISBNResultHandler.java b/android/src/com/google/zxing/client/android/result/ISBNResultHandler.java index fb56fed25..d304fa9be 100644 --- a/android/src/com/google/zxing/client/android/result/ISBNResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/ISBNResultHandler.java @@ -16,44 +16,50 @@ package com.google.zxing.client.android.result; -import android.app.Activity; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import com.google.zxing.client.android.R; import com.google.zxing.client.android.PreferencesActivity; +import com.google.zxing.client.android.R; import com.google.zxing.client.result.ISBNParsedResult; import com.google.zxing.client.result.ParsedResult; -public final class ISBNResultHandler extends ResultHandler { +import android.app.Activity; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; - private static final int[] mButtons = { +/** + * Handles books encoded by their ISBN values. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class ISBNResultHandler extends ResultHandler { + private static final int[] buttons = { R.string.button_product_search, R.string.button_book_search, R.string.button_search_book_contents, R.string.button_custom_product_search, }; - private final String mCustomProductSearch; + private final String customProductSearch; public ISBNResultHandler(Activity activity, ParsedResult result) { super(activity, result); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - mCustomProductSearch = prefs.getString(PreferencesActivity.KEY_CUSTOM_PRODUCT_SEARCH, null); + customProductSearch = prefs.getString(PreferencesActivity.KEY_CUSTOM_PRODUCT_SEARCH, null); } @Override public int getButtonCount() { - return mCustomProductSearch != null && mCustomProductSearch.length() > 0 ? mButtons.length : mButtons.length - 1; + return customProductSearch != null && customProductSearch.length() > 0 ? buttons.length : buttons + .length - 1; } @Override public int getButtonText(int index) { - return mButtons[index]; + return buttons[index]; } @Override public void handleButtonPress(int index) { - ISBNParsedResult isbnResult = (ISBNParsedResult) mResult; + ISBNParsedResult isbnResult = (ISBNParsedResult) result; switch (index) { case 0: openProductSearch(isbnResult.getISBN()); @@ -65,7 +71,7 @@ public final class ISBNResultHandler extends ResultHandler { searchBookContents(isbnResult.getISBN()); break; case 3: - String url = mCustomProductSearch.replace("%s", isbnResult.getISBN()); + String url = customProductSearch.replace("%s", isbnResult.getISBN()); openURL(url); break; } @@ -75,5 +81,4 @@ public final class ISBNResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_isbn; } - } diff --git a/android/src/com/google/zxing/client/android/result/ProductResultHandler.java b/android/src/com/google/zxing/client/android/result/ProductResultHandler.java index 29d8a762c..f497d708e 100644 --- a/android/src/com/google/zxing/client/android/result/ProductResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/ProductResultHandler.java @@ -16,43 +16,48 @@ package com.google.zxing.client.android.result; -import android.app.Activity; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import com.google.zxing.client.android.R; import com.google.zxing.client.android.PreferencesActivity; +import com.google.zxing.client.android.R; import com.google.zxing.client.result.ParsedResult; import com.google.zxing.client.result.ProductParsedResult; -public final class ProductResultHandler extends ResultHandler { +import android.app.Activity; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; - private static final int[] mButtons = { +/** + * Handles generic products which are not books. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class ProductResultHandler extends ResultHandler { + private static final int[] buttons = { R.string.button_product_search, R.string.button_web_search, R.string.button_custom_product_search, }; - private final String mCustomProductSearch; + private final String customProductSearch; public ProductResultHandler(Activity activity, ParsedResult result) { super(activity, result); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - mCustomProductSearch = prefs.getString(PreferencesActivity.KEY_CUSTOM_PRODUCT_SEARCH, null); + customProductSearch = prefs.getString(PreferencesActivity.KEY_CUSTOM_PRODUCT_SEARCH, null); } @Override public int getButtonCount() { - return mCustomProductSearch != null ? mButtons.length : mButtons.length - 1; + return customProductSearch != null ? buttons.length : buttons.length - 1; } @Override public int getButtonText(int index) { - return mButtons[index]; + return buttons[index]; } @Override public void handleButtonPress(int index) { - ProductParsedResult productResult = (ProductParsedResult) mResult; + ProductParsedResult productResult = (ProductParsedResult) result; switch (index) { case 0: openProductSearch(productResult.getNormalizedProductID()); @@ -61,7 +66,7 @@ public final class ProductResultHandler extends ResultHandler { webSearch(productResult.getNormalizedProductID()); break; case 2: - String url = mCustomProductSearch.replace("%s", productResult.getNormalizedProductID()); + String url = customProductSearch.replace("%s", productResult.getNormalizedProductID()); openURL(url); break; } @@ -71,5 +76,4 @@ public final class ProductResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_product; } - } diff --git a/android/src/com/google/zxing/client/android/result/ResultButtonListener.java b/android/src/com/google/zxing/client/android/result/ResultButtonListener.java index e08b78f61..584a3d5a8 100644 --- a/android/src/com/google/zxing/client/android/result/ResultButtonListener.java +++ b/android/src/com/google/zxing/client/android/result/ResultButtonListener.java @@ -22,19 +22,19 @@ import android.widget.Button; /** * Handles the result of barcode decoding in the context of the Android platform, by dispatching the * proper intents to open other activities like GMail, Maps, etc. + * + * @author dswitkin@google.com (Daniel Switkin) */ public final class ResultButtonListener implements Button.OnClickListener { - - final ResultHandler mResultHandler; - final int mIndex; + final ResultHandler resultHandler; + final int index; public ResultButtonListener(ResultHandler resultHandler, int index) { - mResultHandler = resultHandler; - mIndex = index; + this.resultHandler = resultHandler; + this.index = index; } public void onClick(View view) { - mResultHandler.handleButtonPress(mIndex); + resultHandler.handleButtonPress(index); } - } diff --git a/android/src/com/google/zxing/client/android/result/ResultHandler.java b/android/src/com/google/zxing/client/android/result/ResultHandler.java index 337455fd5..c2a367d38 100644 --- a/android/src/com/google/zxing/client/android/result/ResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/ResultHandler.java @@ -16,40 +16,50 @@ package com.google.zxing.client.android.result; +import com.google.zxing.client.android.Contents; +import com.google.zxing.client.android.Intents; +import com.google.zxing.client.android.LocaleManager; +import com.google.zxing.client.android.R; +import com.google.zxing.client.android.SearchBookContentsActivity; +import com.google.zxing.client.result.ParsedResult; +import com.google.zxing.client.result.ParsedResultType; + import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Intent; import android.net.Uri; import android.provider.Contacts; -import com.google.zxing.client.android.Intents; -import com.google.zxing.client.android.R; -import com.google.zxing.client.android.SearchBookContentsActivity; -import com.google.zxing.client.android.LocaleManager; -import com.google.zxing.client.android.Contents; -import com.google.zxing.client.result.ParsedResult; -import com.google.zxing.client.result.ParsedResultType; +import java.text.DateFormat; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Calendar; -import java.text.DateFormat; import java.util.Date; import java.util.GregorianCalendar; +/** + * A base class for the Android-specific barcode handlers. These allow the app to polymorphically + * suggest the appropriate actions for each data type. + * + * This class also contains a bunch of utility methods to take common actions like opening a URL. + * They could easily be moved into a helper object, but it can't be static because the Activity + * instance is needed to launch an intent. + * + * @author dswitkin@google.com (Daniel Switkin) + */ public abstract class ResultHandler { - private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); private static final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); public static final int MAX_BUTTON_COUNT = 4; - protected final ParsedResult mResult; - private final Activity mActivity; + protected final ParsedResult result; + private final Activity activity; protected ResultHandler(Activity activity, ParsedResult result) { - mResult = result; - mActivity = activity; + this.result = result; + this.activity = activity; } /** @@ -81,7 +91,7 @@ public abstract class ResultHandler { * @return The text to be displayed. */ public CharSequence getDisplayContents() { - String contents = mResult.getDisplayResult(); + String contents = result.getDisplayResult(); return contents.replace("\r", ""); } @@ -98,7 +108,7 @@ public abstract class ResultHandler { * @return The parsed type, e.g. URI or ISBN */ public final ParsedResultType getType() { - return mResult.getType(); + return result.getType(); } /** @@ -171,7 +181,7 @@ public abstract class ResultHandler { } public final void shareByEmail(String contents) { - sendEmailFromUri("mailto:", mActivity.getString(R.string.msg_share_subject_line), contents); + sendEmailFromUri("mailto:", activity.getString(R.string.msg_share_subject_line), contents); } public final void sendEmail(String address, String subject, String body) { @@ -188,7 +198,7 @@ public abstract class ResultHandler { } public final void shareBySMS(String contents) { - sendSMSFromUri("smsto:", mActivity.getString(R.string.msg_share_subject_line) + ":\n" + + sendSMSFromUri("smsto:", activity.getString(R.string.msg_share_subject_line) + ":\n" + contents); } @@ -212,7 +222,7 @@ public abstract class ResultHandler { Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(uri)); // The Messaging app needs to see a valid subject or else it will treat this an an SMS. if (subject == null || subject.length() == 0) { - putExtra(intent, "subject", mActivity.getString(R.string.msg_default_mms_subject)); + putExtra(intent, "subject", activity.getString(R.string.msg_default_mms_subject)); } else { putExtra(intent, "subject", subject); } @@ -267,7 +277,7 @@ public abstract class ResultHandler { public final void searchBookContents(String isbn) { Intent intent = new Intent(Intents.SearchBookContents.ACTION); - intent.setClassName(mActivity, SearchBookContentsActivity.class.getName()); + intent.setClassName(activity, SearchBookContentsActivity.class.getName()); putExtra(intent, Intents.SearchBookContents.ISBN, isbn); launchIntent(intent); } @@ -285,11 +295,11 @@ public abstract class ResultHandler { private void launchIntent(Intent intent) { if (intent != null) { try { - mActivity.startActivity(intent); + activity.startActivity(intent); } catch (ActivityNotFoundException e) { - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - builder.setTitle(mActivity.getString(R.string.app_name)); - builder.setMessage(mActivity.getString(R.string.msg_intent_failed)); + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(activity.getString(R.string.app_name)); + builder.setMessage(activity.getString(R.string.msg_intent_failed)); builder.setPositiveButton(R.string.button_ok, null); builder.show(); } @@ -301,5 +311,4 @@ public abstract class ResultHandler { intent.putExtra(key, value); } } - } diff --git a/android/src/com/google/zxing/client/android/result/ResultHandlerFactory.java b/android/src/com/google/zxing/client/android/result/ResultHandlerFactory.java index 3a2277cbf..eeaacca15 100644 --- a/android/src/com/google/zxing/client/android/result/ResultHandlerFactory.java +++ b/android/src/com/google/zxing/client/android/result/ResultHandlerFactory.java @@ -16,14 +16,19 @@ package com.google.zxing.client.android.result; -import android.app.Activity; import com.google.zxing.Result; import com.google.zxing.client.result.ParsedResult; import com.google.zxing.client.result.ParsedResultType; import com.google.zxing.client.result.ResultParser; -public final class ResultHandlerFactory { +import android.app.Activity; +/** + * Manufactures Android-specific handlers based on the barcode content's type. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class ResultHandlerFactory { private ResultHandlerFactory() { } @@ -57,22 +62,6 @@ public final class ResultHandlerFactory { } private static ParsedResult parseResult(Result rawResult) { - ParsedResult result = ResultParser.parseResult(rawResult); - - // Disabled for now. To reactivate, create an AndroidIntentResultHandler. -// if (result.getType().equals(ParsedResultType.TEXT)) { -// String rawText = rawResult.getText(); -// AndroidIntentParsedResult androidResult = AndroidIntentParsedResult.parse(rawText); -// if (androidResult != null) { -// Intent intent = androidResult.getIntent(); -// if (!Intent.ACTION_VIEW.equals(intent.getAction())) { -// // For now, don't take anything that just parses as a View action. A lot -// // of things are accepted as a View action by default. -// result = androidResult; -// } -// } -// } - return result; + return ResultParser.parseResult(rawResult); } - } diff --git a/android/src/com/google/zxing/client/android/result/SMSResultHandler.java b/android/src/com/google/zxing/client/android/result/SMSResultHandler.java index b6efbd6ea..56868bea8 100644 --- a/android/src/com/google/zxing/client/android/result/SMSResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/SMSResultHandler.java @@ -16,15 +16,20 @@ package com.google.zxing.client.android.result; -import android.app.Activity; -import android.telephony.PhoneNumberUtils; import com.google.zxing.client.android.R; import com.google.zxing.client.result.ParsedResult; import com.google.zxing.client.result.SMSParsedResult; -public final class SMSResultHandler extends ResultHandler { +import android.app.Activity; +import android.telephony.PhoneNumberUtils; - private static final int[] mButtons = { +/** + * Handles SMS addresses, offering a choice of composing a new SMS or MMS message. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class SMSResultHandler extends ResultHandler { + private static final int[] buttons = { R.string.button_sms, R.string.button_mms }; @@ -35,17 +40,17 @@ public final class SMSResultHandler extends ResultHandler { @Override public int getButtonCount() { - return mButtons.length; + return buttons.length; } @Override public int getButtonText(int index) { - return mButtons[index]; + return buttons[index]; } @Override public void handleButtonPress(int index) { - SMSParsedResult smsResult = (SMSParsedResult) mResult; + SMSParsedResult smsResult = (SMSParsedResult) result; switch (index) { case 0: sendSMS(smsResult.getNumber(), smsResult.getBody()); @@ -58,7 +63,7 @@ public final class SMSResultHandler extends ResultHandler { @Override public CharSequence getDisplayContents() { - SMSParsedResult smsResult = (SMSParsedResult) mResult; + SMSParsedResult smsResult = (SMSParsedResult) result; StringBuffer contents = new StringBuffer(); ParsedResult.maybeAppend(PhoneNumberUtils.formatNumber(smsResult.getNumber()), contents); ParsedResult.maybeAppend(smsResult.getVia(), contents); @@ -72,5 +77,4 @@ public final class SMSResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_sms; } - } diff --git a/android/src/com/google/zxing/client/android/result/TelResultHandler.java b/android/src/com/google/zxing/client/android/result/TelResultHandler.java index 4ce35d21a..d957d5c15 100644 --- a/android/src/com/google/zxing/client/android/result/TelResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/TelResultHandler.java @@ -16,15 +16,20 @@ package com.google.zxing.client.android.result; -import android.app.Activity; -import android.telephony.PhoneNumberUtils; import com.google.zxing.client.android.R; import com.google.zxing.client.result.ParsedResult; import com.google.zxing.client.result.TelParsedResult; -public final class TelResultHandler extends ResultHandler { +import android.app.Activity; +import android.telephony.PhoneNumberUtils; - private static final int[] mButtons = { +/** + * Offers relevant actions for telephone numbers. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class TelResultHandler extends ResultHandler { + private static final int[] buttons = { R.string.button_dial, R.string.button_add_contact }; @@ -35,17 +40,17 @@ public final class TelResultHandler extends ResultHandler { @Override public int getButtonCount() { - return mButtons.length; + return buttons.length; } @Override public int getButtonText(int index) { - return mButtons[index]; + return buttons[index]; } @Override public void handleButtonPress(int index) { - TelParsedResult telResult = (TelParsedResult) mResult; + TelParsedResult telResult = (TelParsedResult) result; switch (index) { case 0: dialPhoneFromUri(telResult.getTelURI()); @@ -61,7 +66,7 @@ public final class TelResultHandler extends ResultHandler { // Overriden so we can take advantage of Android's phone number hyphenation routines. @Override public CharSequence getDisplayContents() { - String contents = mResult.getDisplayResult(); + String contents = result.getDisplayResult(); contents = contents.replace("\r", ""); return PhoneNumberUtils.formatNumber(contents); } @@ -70,5 +75,4 @@ public final class TelResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_tel; } - } diff --git a/android/src/com/google/zxing/client/android/result/TextResultHandler.java b/android/src/com/google/zxing/client/android/result/TextResultHandler.java index c2a410128..010b76c8a 100644 --- a/android/src/com/google/zxing/client/android/result/TextResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/TextResultHandler.java @@ -16,16 +16,18 @@ package com.google.zxing.client.android.result; -import android.app.Activity; import com.google.zxing.client.android.R; import com.google.zxing.client.result.ParsedResult; +import android.app.Activity; + /** - * This class handles TextParsedResult as well as unknown formats. + * This class handles TextParsedResult as well as unknown formats. It's the fallback handler. + * + * @author dswitkin@google.com (Daniel Switkin) */ public final class TextResultHandler extends ResultHandler { - - private static final int[] mButtons = { + private static final int[] buttons = { R.string.button_web_search, R.string.button_share_by_email, R.string.button_share_by_sms @@ -37,25 +39,25 @@ public final class TextResultHandler extends ResultHandler { @Override public int getButtonCount() { - return mButtons.length; + return buttons.length; } @Override public int getButtonText(int index) { - return mButtons[index]; + return buttons[index]; } @Override public void handleButtonPress(int index) { switch (index) { case 0: - webSearch(mResult.getDisplayResult()); + webSearch(result.getDisplayResult()); break; case 1: - shareByEmail(mResult.getDisplayResult()); + shareByEmail(result.getDisplayResult()); break; case 2: - shareBySMS(mResult.getDisplayResult()); + shareBySMS(result.getDisplayResult()); break; } } @@ -64,5 +66,4 @@ public final class TextResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_text; } - } diff --git a/android/src/com/google/zxing/client/android/result/URIResultHandler.java b/android/src/com/google/zxing/client/android/result/URIResultHandler.java index 9d1c2e53f..cd4e11eb3 100644 --- a/android/src/com/google/zxing/client/android/result/URIResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/URIResultHandler.java @@ -16,14 +16,19 @@ package com.google.zxing.client.android.result; -import android.app.Activity; import com.google.zxing.client.android.R; import com.google.zxing.client.result.ParsedResult; import com.google.zxing.client.result.URIParsedResult; -public final class URIResultHandler extends ResultHandler { +import android.app.Activity; - private static final int[] mButtons = { +/** + * Offers appropriate actions for URLS. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class URIResultHandler extends ResultHandler { + private static final int[] buttons = { R.string.button_open_browser, R.string.button_share_by_email, R.string.button_share_by_sms @@ -35,17 +40,17 @@ public final class URIResultHandler extends ResultHandler { @Override public int getButtonCount() { - return mButtons.length; + return buttons.length; } @Override public int getButtonText(int index) { - return mButtons[index]; + return buttons[index]; } @Override public void handleButtonPress(int index) { - URIParsedResult uriResult = (URIParsedResult) mResult; + URIParsedResult uriResult = (URIParsedResult) result; switch (index) { case 0: openURL(uriResult.getURI()); @@ -63,5 +68,4 @@ public final class URIResultHandler extends ResultHandler { public int getDisplayTitle() { return R.string.result_uri; } - }