Commit ecb1f5eb by 倪力烽

1. 完成调度功能框架的搭建

2. 完成调度界面的编写
3. 提交功能逻辑尚未编写
4. 需要结合接口进行测试
5. 二维码框架搭建完成
parent 465504c7
......@@ -48,4 +48,5 @@ dependencies {
compile 'com.android.support:design:25.3.1'
compile files('/Users/ni/Documents/lierda/lierda_project/EBike-Administrer-Tool/libs/android-async-http-1.4.6.jar')
compile files('/Users/ni/Documents/lierda/lierda_project/EBike-Administrer-Tool/libs/org.apache.http.legacy.jar')
compile files('/Users/ni/Documents/lierda/lierda_project/EBike-Administrer-Tool/libs/zixing-core-3.2.0.jar')
}
......@@ -5,6 +5,15 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
android:name="android.hardware.camera.front" android:required="false" />
<uses-feature
android:name="android.hardware.camera.flash"
android:required="false" />
<application
android:name=".MyApplication"
android:allowBackup="true"
......@@ -28,6 +37,18 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.google.zxing.client.android.CaptureActivity"
android:screenOrientation="portrait"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:theme="@android:style/Theme.NoTitleBar" >
<intent-filter>
<action android:name="com.google.zxing.client.android.SCAN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
/*
* Copyright (C) 2012 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 android.content.Context;
import android.content.SharedPreferences;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.preference.PreferenceManager;
import com.google.zxing.client.android.camera.CameraManager;
import com.google.zxing.client.android.camera.FrontLightMode;
/**
* Detects ambient light and switches on the front light when very dark, and off again when sufficiently light.
*
* @author Sean Owen
* @author Nikolaus Huber
*/
final class AmbientLightManager implements SensorEventListener {
private static final float TOO_DARK_LUX = 45.0f;
private static final float BRIGHT_ENOUGH_LUX = 450.0f;
private final Context context;
private CameraManager cameraManager;
private Sensor lightSensor;
AmbientLightManager(Context context) {
this.context = context;
}
void start(CameraManager cameraManager) {
this.cameraManager = cameraManager;
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
if (FrontLightMode.readPref(sharedPrefs) == FrontLightMode.AUTO) {
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if (lightSensor != null) {
sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
}
void stop() {
if (lightSensor != null) {
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
sensorManager.unregisterListener(this);
cameraManager = null;
lightSensor = null;
}
}
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
float ambientLightLux = sensorEvent.values[0];
if (cameraManager != null) {
if (ambientLightLux <= TOO_DARK_LUX) {
cameraManager.setTorch(true);
} else if (ambientLightLux >= BRIGHT_ENOUGH_LUX) {
cameraManager.setTorch(false);
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// do nothing
}
}
/*
* Copyright (C) 2010 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 android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.Log;
import com.senthink.ebike_administrer_tool.R;
import java.io.Closeable;
import java.io.IOException;
/**
* Manages beeps and vibrations for {@link CaptureActivity}.
*/
final class BeepManager implements MediaPlayer.OnErrorListener, Closeable {
private static final String TAG = BeepManager.class.getSimpleName();
private static final float BEEP_VOLUME = 0.10f;
private static final long VIBRATE_DURATION = 200L;
private final Activity activity;
private MediaPlayer mediaPlayer;
private boolean playBeep;
private boolean vibrate;
BeepManager(Activity activity) {
this.activity = activity;
this.mediaPlayer = null;
updatePrefs();
}
synchronized void updatePrefs() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
playBeep = shouldBeep(prefs, activity);
vibrate = prefs.getBoolean(PreferencesActivity.KEY_VIBRATE, false);
if (playBeep && mediaPlayer == null) {
// The volume on STREAM_SYSTEM is not adjustable, and users found it too loud,
// so we now play on the music stream.
activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
mediaPlayer = buildMediaPlayer(activity);
}
}
synchronized void playBeepSoundAndVibrate() {
if (playBeep && mediaPlayer != null) {
mediaPlayer.start();
}
if (vibrate) {
Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(VIBRATE_DURATION);
}
}
private static boolean shouldBeep(SharedPreferences prefs, Context activity) {
boolean shouldPlayBeep = prefs.getBoolean(PreferencesActivity.KEY_PLAY_BEEP, true);
if (shouldPlayBeep) {
// See if sound settings overrides this
AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
shouldPlayBeep = false;
}
}
return shouldPlayBeep;
}
private MediaPlayer buildMediaPlayer(Context activity) {
MediaPlayer mediaPlayer = new MediaPlayer();
try {
AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.beep);
try {
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
} finally {
file.close();
}
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setLooping(false);
mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
mediaPlayer.prepare();
return mediaPlayer;
} catch (IOException ioe) {
Log.w(TAG, ioe);
mediaPlayer.release();
return null;
}
}
@Override
public synchronized boolean onError(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
// we are finished, so put up an appropriate error toast if required and finish
activity.finish();
} else {
// possibly media player error, so release and recreate
close();
updatePrefs();
}
return true;
}
@Override
public synchronized void close() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
}
/*
* 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;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.senthink.ebike_administrer_tool.R;
import com.google.zxing.client.android.camera.CameraManager;
import com.google.zxing.client.android.result.ResultHandlerFactory;
import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Map;
/**
* This activity opens the camera and does the actual scanning on a background
* thread. It draws a viewfinder to help the user place the barcode correctly,
* shows feedback as the image processing is happening, and then overlays the
* results when a scan is successful.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen
*/
public final class CaptureActivity extends Activity implements
SurfaceHolder.Callback {
private static final String TAG = CaptureActivity.class.getSimpleName();
private static final String[] ZXING_URLS = {
"http://zxing.appspot.com/scan", "zxing://scan/" };
public static final int HISTORY_REQUEST_CODE = 0x0000bacc;
private static final Collection<ResultMetadataType> DISPLAYABLE_METADATA_TYPES = EnumSet
.of(ResultMetadataType.ISSUE_NUMBER,
ResultMetadataType.SUGGESTED_PRICE,
ResultMetadataType.ERROR_CORRECTION_LEVEL,
ResultMetadataType.POSSIBLE_COUNTRY);
private CameraManager cameraManager;
private CaptureActivityHandler handler;
private Result savedResultToShow;
private ViewfinderView viewfinderView;
private Result lastResult;
private boolean hasSurface;
private boolean copyToClipboard;
private IntentSource source;
private ScanFromWebPageManager scanFromWebPageManager;
private Collection<BarcodeFormat> decodeFormats;
private Map<DecodeHintType, ?> decodeHints;
private String characterSet;
private InactivityTimer inactivityTimer;
private BeepManager beepManager;
private ImageButton imageButton_back;
private RelativeLayout mHeader;
private TextView mTitle;
private AmbientLightManager ambientLightManager;
ViewfinderView getViewfinderView() {
return viewfinderView;
}
public Handler getHandler() {
return handler;
}
CameraManager getCameraManager() {
return cameraManager;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.logistic_capture);
hasSurface = false;
inactivityTimer = new InactivityTimer(this);
beepManager = new BeepManager(this);
ambientLightManager = new AmbientLightManager(this);
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
imageButton_back = (ImageButton) findViewById(R.id.capture_imageview_back);
imageButton_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
mHeader = (RelativeLayout) findViewById(R.id.capture_header_rl);
mTitle = (TextView) findViewById(R.id.capture_title_tv);
Bundle bundle = this.getIntent().getExtras();
if (bundle != null && bundle.containsKey("action")) {
mHeader.setBackgroundColor(Color.BLACK);
mHeader.getBackground().setAlpha(80);
mTitle.setText("扫一扫");
}
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}
@Override
protected void onResume() {
super.onResume();
// CameraManager must be initialized here, not in onCreate(). This is
// necessary because we don't
// want to open the camera driver and measure the screen size if we're
// going to show the help on
// first launch. That led to bugs where the scanning rectangle was the
// wrong size and partially
// off screen.
cameraManager = new CameraManager(getApplication());
viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
viewfinderView.setCameraManager(cameraManager);
handler = null;
lastResult = null;
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(this);
/*
* if
* (prefs.getBoolean(PreferencesActivity.KEY_DISABLE_AUTO_ORIENTATION,
* true)) { setRequestedOrientation(getCurrentOrientation()); } else {
* setRequestedOrientation
* (ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); }
*/
resetStatusView();
beepManager.updatePrefs();
ambientLightManager.start(cameraManager);
inactivityTimer.onResume();
Intent intent = getIntent();
copyToClipboard = prefs.getBoolean(
PreferencesActivity.KEY_COPY_TO_CLIPBOARD, true)
&& (intent == null || intent.getBooleanExtra(
Intents.Scan.SAVE_HISTORY, true));
source = IntentSource.NONE;
scanFromWebPageManager = null;
decodeFormats = null;
characterSet = null;
if (intent != null) {
String action = intent.getAction();
String dataString = intent.getDataString();
if (Intents.Scan.ACTION.equals(action)) {
// Scan the formats the intent requested, and return the result
// to the calling activity.
source = IntentSource.NATIVE_APP_INTENT;
decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);
decodeHints = DecodeHintManager.parseDecodeHints(intent);
if (intent.hasExtra(Intents.Scan.WIDTH)
&& intent.hasExtra(Intents.Scan.HEIGHT)) {
int width = intent.getIntExtra(Intents.Scan.WIDTH, 0);
int height = intent.getIntExtra(Intents.Scan.HEIGHT, 0);
if (width > 0 && height > 0) {
cameraManager.setManualFramingRect(width, height);
}
}
if (intent.hasExtra(Intents.Scan.CAMERA_ID)) {
int cameraId = intent.getIntExtra(Intents.Scan.CAMERA_ID,
-1);
if (cameraId >= 0) {
cameraManager.setManualCameraId(cameraId);
}
}
String customPromptMessage = intent
.getStringExtra(Intents.Scan.PROMPT_MESSAGE);
if (customPromptMessage != null) {
//statusView.setText(customPromptMessage);
}
} else if (dataString != null
&& dataString.contains("http://www.google")
&& dataString.contains("/m/products/scan")) {
// Scan only products and send the result to mobile Product
// Search.
source = IntentSource.PRODUCT_SEARCH_LINK;
decodeFormats = DecodeFormatManager.PRODUCT_FORMATS;
} else if (isZXingURL(dataString)) {
// Scan formats requested in query string (all formats if none
// specified).
// If a return URL is specified, send the results there.
// Otherwise, handle it ourselves.
source = IntentSource.ZXING_LINK;
Uri inputUri = Uri.parse(dataString);
scanFromWebPageManager = new ScanFromWebPageManager(inputUri);
decodeFormats = DecodeFormatManager
.parseDecodeFormats(inputUri);
// Allow a sub-set of the hints to be specified by the caller.
decodeHints = DecodeHintManager.parseDecodeHints(inputUri);
}
characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);
}
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
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);
} else {
// Install the callback and wait for surfaceCreated() to init the
// camera.
surfaceHolder.addCallback(this);
}
}
private int getCurrentOrientation() {
int rotation = getWindowManager().getDefaultDisplay().getRotation();
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
switch (rotation) {
case Surface.ROTATION_0:
case Surface.ROTATION_90:
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
default:
return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
}
} else {
switch (rotation) {
case Surface.ROTATION_0:
case Surface.ROTATION_270:
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
default:
return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
}
}
}
private static boolean isZXingURL(String dataString) {
if (dataString == null) {
return false;
}
for (String url : ZXING_URLS) {
if (dataString.startsWith(url)) {
return true;
}
}
return false;
}
@Override
protected void onPause() {
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
inactivityTimer.onPause();
ambientLightManager.stop();
beepManager.close();
cameraManager.closeDriver();
// historyManager = null; // Keep for onActivityResult
if (!hasSurface) {
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
surfaceHolder.removeCallback(this);
}
super.onPause();
}
@Override
protected void onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (source == IntentSource.NATIVE_APP_INTENT) {
setResult(RESULT_CANCELED);
finish();
return true;
}
if ((source == IntentSource.NONE || source == IntentSource.ZXING_LINK)
&& lastResult != null) {
restartPreviewAfterDelay(0L);
return true;
}
break;
case KeyEvent.KEYCODE_FOCUS:
case KeyEvent.KEYCODE_CAMERA:
// Handle these events so they don't launch the Camera app
return true;
// Use volume up/down to turn on light
case KeyEvent.KEYCODE_VOLUME_DOWN:
cameraManager.setTorch(false);
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
cameraManager.setTorch(true);
return true;
}
return super.onKeyDown(keyCode, event);
}
private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result) {
// Bitmap isn't used yet -- will be used soon
if (handler == null) {
savedResultToShow = result;
} else {
if (result != null) {
savedResultToShow = result;
}
if (savedResultToShow != null) {
Message message = Message.obtain(handler,
R.id.decode_succeeded, savedResultToShow);
handler.sendMessage(message);
}
savedResultToShow = null;
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
Log.e(TAG,
"*** WARNING *** surfaceCreated() gave us a null surface!");
}
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
/**
* A valid barcode has been found, so give an indication of success and show
* the results.
*
* @param rawResult
* The contents of the barcode.
* @param scaleFactor
* amount by which thumbnail was scaled
* @param barcode
* A greyscale bitmap of the camera data which was decoded.
*/
public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
inactivityTimer.onActivity();
lastResult = rawResult;
ResultHandlerFactory.makeResultHandler(
this, rawResult);
boolean fromLiveScan = barcode != null;
if (fromLiveScan) {
// historyManager.addHistoryItem(rawResult, resultHandler);
// Then not from history, so beep/vibrate and we have an image to
// draw on
beepManager.playBeepSoundAndVibrate();
// drawResultPoints(barcode, scaleFactor, rawResult);
Toast.makeText(this, "扫码成功", Toast.LENGTH_SHORT).show();
Intent intent = getIntent();
intent.putExtra("codedContent", rawResult.getText());
// intent.putExtra("codedBitmap", barcode);
setResult(1070, intent);
finish();
}
}
private void initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
Log.w(TAG,
"initCamera() while already open -- late SurfaceView callback?");
return;
}
try {
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a
// RuntimeException.
if (handler == null) {
handler = new CaptureActivityHandler(this, decodeFormats,
decodeHints, characterSet, cameraManager);
}
decodeOrStoreSavedBitmap(null, null);
} catch (IOException ioe) {
Log.w(TAG, ioe);
displayFrameworkBugMessageAndExit();
} catch (RuntimeException e) {
// Barcode Scanner has seen crashes in the wild of this variety:
// java.?lang.?RuntimeException: Fail to connect to camera service
Log.w(TAG, "Unexpected error initializing camera", e);
displayFrameworkBugMessageAndExit();
}
}
private void displayFrameworkBugMessageAndExit() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.app_name));
builder.setMessage(getString(R.string.msg_camera_framework_bug));
builder.setPositiveButton("OK", new FinishListener(this));
builder.setOnCancelListener(new FinishListener(this));
builder.show();
}
public void restartPreviewAfterDelay(long delayMS) {
if (handler != null) {
handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
}
resetStatusView();
}
private void resetStatusView() {
/*resultView.setVisibility(View.GONE);
statusView.setText(R.string.msg_default_status);
statusView.setVisibility(View.VISIBLE);*/
viewfinderView.setVisibility(View.VISIBLE);
lastResult = null;
}
public void drawViewfinder() {
viewfinderView.drawViewfinder();
}
}
/*
* 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;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Browser;
import android.util.Log;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.google.zxing.client.android.camera.CameraManager;
import com.senthink.ebike_administrer_tool.R;
import java.util.Collection;
import java.util.Map;
/**
* 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 static final String TAG = CaptureActivityHandler.class.getSimpleName();
private final CaptureActivity activity;
private final DecodeThread decodeThread;
private State state;
private final CameraManager cameraManager;
private enum State {
PREVIEW,
SUCCESS,
DONE
}
CaptureActivityHandler(CaptureActivity activity,
Collection<BarcodeFormat> decodeFormats,
Map<DecodeHintType,?> baseHints,
String characterSet,
CameraManager cameraManager) {
this.activity = activity;
decodeThread = new DecodeThread(activity, decodeFormats, baseHints, characterSet,
new ViewfinderResultPointCallback(activity.getViewfinderView()));
decodeThread.start();
state = State.SUCCESS;
// Start ourselves capturing previews and decoding.
this.cameraManager = cameraManager;
cameraManager.startPreview();
restartPreviewAndDecode();
}
@Override
public void handleMessage(Message message) {
switch (message.what) {
case R.id.restart_preview:
restartPreviewAndDecode();
break;
case R.id.decode_succeeded:
state = State.SUCCESS;
Bundle bundle = message.getData();
Bitmap barcode = null;
float scaleFactor = 1.0f;
if (bundle != null) {
byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
if (compressedBitmap != null) {
barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
// Mutable copy:
barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
}
scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);
}
activity.handleDecode((Result) message.obj, barcode, scaleFactor);
break;
case R.id.decode_failed:
// We're decoding as fast as possible, so when one decode fails, start another.
state = State.PREVIEW;
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
break;
case R.id.return_scan_result:
activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
activity.finish();
break;
case R.id.launch_product_query:
String url = (String) message.obj;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
intent.setData(Uri.parse(url));
ResolveInfo resolveInfo =
activity.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
String browserPackageName = null;
if (resolveInfo != null && resolveInfo.activityInfo != null) {
browserPackageName = resolveInfo.activityInfo.packageName;
Log.d(TAG, "Using browser in package " + browserPackageName);
}
// Needed for default Android browser / Chrome only apparently
if ("com.android.browser".equals(browserPackageName) || "com.android.chrome".equals(browserPackageName)) {
intent.setPackage(browserPackageName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackageName);
}
try {
activity.startActivity(intent);
} catch (ActivityNotFoundException ignored) {
Log.w(TAG, "Can't find anything to handle VIEW of URI " + url);
}
break;
}
}
public void quitSynchronously() {
state = State.DONE;
cameraManager.stopPreview();
Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
quit.sendToTarget();
try {
// Wait at most half a second; should be enough time, and onPause() will timeout quickly
decodeThread.join(500L);
} catch (InterruptedException e) {
// continue
}
// Be absolutely sure we don't send any queued up messages
removeMessages(R.id.decode_succeeded);
removeMessages(R.id.decode_failed);
}
private void restartPreviewAndDecode() {
if (state == State.SUCCESS) {
state = State.PREVIEW;
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
activity.drawViewfinder();
}
}
}
/*
* 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;
import android.provider.ContactsContract;
/**
* 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() {
}
public static final class Type {
/**
* Plain text. Use Intent.putExtra(DATA, string). This can be used for URLs too, but string
* must include "http://" or "https://".
*/
public static final String TEXT = "TEXT_TYPE";
/**
* An email type. Use Intent.putExtra(DATA, string) where string is the email address.
*/
public static final String EMAIL = "EMAIL_TYPE";
/**
* Use Intent.putExtra(DATA, string) where string is the phone number to call.
*/
public static final String PHONE = "PHONE_TYPE";
/**
* An SMS type. Use Intent.putExtra(DATA, string) where string is the number to SMS.
*/
public static final String SMS = "SMS_TYPE";
/**
* A contact. Send a request to encode it as follows:
* {@code
* import android.provider.Contacts;
*
* Intent intent = new Intent(Intents.Encode.ACTION);
* intent.putExtra(Intents.Encode.TYPE, CONTACT);
* Bundle bundle = new Bundle();
* bundle.putString(ContactsContract.Intents.Insert.NAME, "Jenny");
* bundle.putString(ContactsContract.Intents.Insert.PHONE, "8675309");
* bundle.putString(ContactsContract.Intents.Insert.EMAIL, "jenny@the80s.com");
* bundle.putString(ContactsContract.Intents.Insert.POSTAL, "123 Fake St. San Francisco, CA 94102");
* intent.putExtra(Intents.Encode.DATA, bundle);
* }
*/
public static final String CONTACT = "CONTACT_TYPE";
/**
* A geographic location. Use as follows:
* Bundle bundle = new Bundle();
* bundle.putFloat("LAT", latitude);
* bundle.putFloat("LONG", longitude);
* intent.putExtra(Intents.Encode.DATA, bundle);
*/
public static final String LOCATION = "LOCATION_TYPE";
private Type() {
}
}
public static final String URL_KEY = "URL_KEY";
public static final String NOTE_KEY = "NOTE_KEY";
/**
* When using Type.CONTACT, these arrays provide the keys for adding or retrieving multiple
* phone numbers and addresses.
*/
public static final String[] PHONE_KEYS = {
ContactsContract.Intents.Insert.PHONE,
ContactsContract.Intents.Insert.SECONDARY_PHONE,
ContactsContract.Intents.Insert.TERTIARY_PHONE
};
public static final String[] PHONE_TYPE_KEYS = {
ContactsContract.Intents.Insert.PHONE_TYPE,
ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE,
ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE
};
public static final String[] EMAIL_KEYS = {
ContactsContract.Intents.Insert.EMAIL,
ContactsContract.Intents.Insert.SECONDARY_EMAIL,
ContactsContract.Intents.Insert.TERTIARY_EMAIL
};
public static final String[] EMAIL_TYPE_KEYS = {
ContactsContract.Intents.Insert.EMAIL_TYPE,
ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE,
ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE
};
}
/*
* Copyright (C) 2010 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 android.content.Intent;
import android.net.Uri;
import com.google.zxing.BarcodeFormat;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
final class DecodeFormatManager {
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
static final Set<BarcodeFormat> PRODUCT_FORMATS;
static final Set<BarcodeFormat> INDUSTRIAL_FORMATS;
private static final Set<BarcodeFormat> ONE_D_FORMATS;
static final Set<BarcodeFormat> QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
static final Set<BarcodeFormat> DATA_MATRIX_FORMATS = EnumSet.of(BarcodeFormat.DATA_MATRIX);
static final Set<BarcodeFormat> AZTEC_FORMATS = EnumSet.of(BarcodeFormat.AZTEC);
static final Set<BarcodeFormat> PDF417_FORMATS = EnumSet.of(BarcodeFormat.PDF_417);
static {
PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A,
BarcodeFormat.UPC_E,
BarcodeFormat.EAN_13,
BarcodeFormat.EAN_8,
BarcodeFormat.RSS_14,
BarcodeFormat.RSS_EXPANDED);
INDUSTRIAL_FORMATS = EnumSet.of(BarcodeFormat.CODE_39,
BarcodeFormat.CODE_93,
BarcodeFormat.CODE_128,
BarcodeFormat.ITF,
BarcodeFormat.CODABAR);
ONE_D_FORMATS = EnumSet.copyOf(PRODUCT_FORMATS);
ONE_D_FORMATS.addAll(INDUSTRIAL_FORMATS);
}
private static final Map<String,Set<BarcodeFormat>> FORMATS_FOR_MODE;
static {
FORMATS_FOR_MODE = new HashMap<String, Set<BarcodeFormat>>();
FORMATS_FOR_MODE.put(Intents.Scan.ONE_D_MODE, ONE_D_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.PRODUCT_MODE, PRODUCT_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.QR_CODE_MODE, QR_CODE_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.DATA_MATRIX_MODE, DATA_MATRIX_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.AZTEC_MODE, AZTEC_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.PDF417_MODE, PDF417_FORMATS);
}
private DecodeFormatManager() {}
static Set<BarcodeFormat> parseDecodeFormats(Intent intent) {
Iterable<String> scanFormats = null;
CharSequence scanFormatsString = intent.getStringExtra(Intents.Scan.FORMATS);
if (scanFormatsString != null) {
scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
}
return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
}
static Set<BarcodeFormat> parseDecodeFormats(Uri inputUri) {
List<String> formats = inputUri.getQueryParameters(Intents.Scan.FORMATS);
if (formats != null && formats.size() == 1 && formats.get(0) != null){
formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));
}
return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));
}
private static Set<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats, String decodeMode) {
if (scanFormats != null) {
Set<BarcodeFormat> formats = EnumSet.noneOf(BarcodeFormat.class);
try {
for (String format : scanFormats) {
formats.add(BarcodeFormat.valueOf(format));
}
return formats;
} catch (IllegalArgumentException iae) {
// ignore it then
}
}
if (decodeMode != null) {
return FORMATS_FOR_MODE.get(decodeMode);
}
return null;
}
}
/*
* Copyright (C) 2010 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 android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.senthink.ebike_administrer_tool.R;
import java.io.ByteArrayOutputStream;
import java.util.Map;
final class DecodeHandler extends Handler {
private static final String TAG = DecodeHandler.class.getSimpleName();
private final CaptureActivity activity;
private final MultiFormatReader multiFormatReader;
private boolean running = true;
DecodeHandler(CaptureActivity activity, Map<DecodeHintType,Object> hints) {
multiFormatReader = new MultiFormatReader();
multiFormatReader.setHints(hints);
this.activity = activity;
}
@Override
public void handleMessage(Message message) {
if (!running) {
return;
}
switch (message.what) {
case R.id.decode:
decode((byte[]) message.obj, message.arg1, message.arg2);
break;
case R.id.quit:
running = false;
Looper.myLooper().quit();
break;
}
}
/**
* Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
* reuse the same reader objects from one decode to the next.
*
* @param data The YUV preview frame.
* @param width The width of the preview frame.
* @param height The height of the preview frame.
*/
private void decode(byte[] data, int width, int height) {
long start = System.currentTimeMillis();
Result rawResult = null;
// PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width;
width = height;
height = tmp;
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(rotatedData, width, height);
if (source != null) {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
rawResult = multiFormatReader.decodeWithState(bitmap);
} catch (ReaderException re) {
// continue
} finally {
multiFormatReader.reset();
}
}
Handler handler = activity.getHandler();
if (rawResult != null) {
// Don't log the barcode contents for security.
long end = System.currentTimeMillis();
Log.d(TAG, "Found barcode in " + (end - start) + " ms");
if (handler != null) {
Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
Bundle bundle = new Bundle();
bundleThumbnail(source, bundle);
message.setData(bundle);
message.sendToTarget();
}
} else {
if (handler != null) {
Message message = Message.obtain(handler, R.id.decode_failed);
message.sendToTarget();
}
}
}
private static void bundleThumbnail(PlanarYUVLuminanceSource source, Bundle bundle) {
int[] pixels = source.renderThumbnail();
int width = source.getThumbnailWidth();
int height = source.getThumbnailHeight();
Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());
bundle.putFloat(DecodeThread.BARCODE_SCALED_FACTOR, (float) width / source.getWidth());
}
}
/*
* Copyright (C) 2013 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 android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import com.google.zxing.DecodeHintType;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @author Lachezar Dobrev
*/
final class DecodeHintManager {
private static final String TAG = DecodeHintManager.class.getSimpleName();
// This pattern is used in decoding integer arrays.
private static final Pattern COMMA = Pattern.compile(",");
private DecodeHintManager() {}
/**
* <p>Split a query string into a list of name-value pairs.</p>
*
* <p>This is an alternative to the {@link Uri#getQueryParameterNames()} and
* {@link Uri#getQueryParameters(String)}, which are quirky and not suitable
* for exist-only Uri parameters.</p>
*
* <p>This method ignores multiple parameters with the same name and returns the
* first one only. This is technically incorrect, but should be acceptable due
* to the method of processing Hints: no multiple values for a hint.</p>
*
* @param query query to split
* @return name-value pairs
*/
private static Map<String,String> splitQuery(String query) {
Map<String,String> map = new HashMap<String, String>();
int pos = 0;
while (pos < query.length()) {
if (query.charAt(pos) == '&') {
// Skip consecutive ampersand separators.
pos ++;
continue;
}
int amp = query.indexOf('&', pos);
int equ = query.indexOf('=', pos);
if (amp < 0) {
// This is the last element in the query, no more ampersand elements.
String name;
String text;
if (equ < 0) {
// No equal sign
name = query.substring(pos);
name = name.replace('+', ' '); // Preemptively decode +
name = Uri.decode(name);
text = "";
} else {
// Split name and text.
name = query.substring(pos, equ);
name = name.replace('+', ' '); // Preemptively decode +
name = Uri.decode(name);
text = query.substring(equ + 1);
text = text.replace('+', ' '); // Preemptively decode +
text = Uri.decode(text);
}
if (!map.containsKey(name)) {
map.put(name, text);
}
break;
}
if (equ < 0 || equ > amp) {
// No equal sign until the &: this is a simple parameter with no value.
String name = query.substring(pos, amp);
name = name.replace('+', ' '); // Preemptively decode +
name = Uri.decode(name);
if (!map.containsKey(name)) {
map.put(name, "");
}
pos = amp + 1;
continue;
}
String name = query.substring(pos, equ);
name = name.replace('+', ' '); // Preemptively decode +
name = Uri.decode(name);
String text = query.substring(equ+1, amp);
text = text.replace('+', ' '); // Preemptively decode +
text = Uri.decode(text);
if (!map.containsKey(name)) {
map.put(name, text);
}
pos = amp + 1;
}
return map;
}
static Map<DecodeHintType,?> parseDecodeHints(Uri inputUri) {
String query = inputUri.getEncodedQuery();
if (query == null || query.isEmpty()) {
return null;
}
// Extract parameters
Map<String, String> parameters = splitQuery(query);
Map<DecodeHintType, Object> hints = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
for (DecodeHintType hintType: DecodeHintType.values()) {
if (hintType == DecodeHintType.CHARACTER_SET ||
hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||
hintType == DecodeHintType.POSSIBLE_FORMATS) {
continue; // This hint is specified in another way
}
String parameterName = hintType.name();
String parameterText = parameters.get(parameterName);
if (parameterText == null) {
continue;
}
if (hintType.getValueType().equals(Object.class)) {
// This is an unspecified type of hint content. Use the value as is.
// TODO: Can we make a different assumption on this?
hints.put(hintType, parameterText);
continue;
}
if (hintType.getValueType().equals(Void.class)) {
// Void hints are just flags: use the constant specified by DecodeHintType
hints.put(hintType, Boolean.TRUE);
continue;
}
if (hintType.getValueType().equals(String.class)) {
// A string hint: use the decoded value.
hints.put(hintType, parameterText);
continue;
}
if (hintType.getValueType().equals(Boolean.class)) {
// A boolean hint: a few values for false, everything else is true.
// An empty parameter is simply a flag-style parameter, assuming true
if (parameterText.isEmpty()) {
hints.put(hintType, Boolean.TRUE);
} else if ("0".equals(parameterText) ||
"false".equalsIgnoreCase(parameterText) ||
"no".equalsIgnoreCase(parameterText)) {
hints.put(hintType, Boolean.FALSE);
} else {
hints.put(hintType, Boolean.TRUE);
}
continue;
}
if (hintType.getValueType().equals(int[].class)) {
// An integer array. Used to specify valid lengths.
// Strip a trailing comma as in Java style array initialisers.
if (!parameterText.isEmpty() && parameterText.charAt(parameterText.length() - 1) == ',') {
parameterText = parameterText.substring(0, parameterText.length() - 1);
}
String[] values = COMMA.split(parameterText);
int[] array = new int[values.length];
for (int i = 0; i < values.length; i++) {
try {
array[i] = Integer.parseInt(values[i]);
} catch (NumberFormatException ignored) {
Log.w(TAG, "Skipping array of integers hint " + hintType + " due to invalid numeric value: '" + values[i] + '\'');
array = null;
break;
}
}
if (array != null) {
hints.put(hintType, array);
}
continue;
}
Log.w(TAG, "Unsupported hint type '" + hintType + "' of type " + hintType.getValueType());
}
Log.i(TAG, "Hints from the URI: " + hints);
return hints;
}
static Map<DecodeHintType, Object> parseDecodeHints(Intent intent) {
Bundle extras = intent.getExtras();
if (extras == null || extras.isEmpty()) {
return null;
}
Map<DecodeHintType,Object> hints = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
for (DecodeHintType hintType: DecodeHintType.values()) {
if (hintType == DecodeHintType.CHARACTER_SET ||
hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||
hintType == DecodeHintType.POSSIBLE_FORMATS) {
continue; // This hint is specified in another way
}
String hintName = hintType.name();
if (extras.containsKey(hintName)) {
if (hintType.getValueType().equals(Void.class)) {
// Void hints are just flags: use the constant specified by the DecodeHintType
hints.put(hintType, Boolean.TRUE);
} else {
Object hintData = extras.get(hintName);
if (hintType.getValueType().isInstance(hintData)) {
hints.put(hintType, hintData);
} else {
Log.w(TAG, "Ignoring hint " + hintType + " because it is not assignable from " + hintData);
}
}
}
}
Log.i(TAG, "Hints from the Intent: " + hints);
return hints;
}
}
/*
* 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;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.util.Log;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.ResultPointCallback;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
/**
* 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";
public static final String BARCODE_SCALED_FACTOR = "barcode_scaled_factor";
private final CaptureActivity activity;
private final Map<DecodeHintType,Object> hints;
private Handler handler;
private final CountDownLatch handlerInitLatch;
DecodeThread(CaptureActivity activity,
Collection<BarcodeFormat> decodeFormats,
Map<DecodeHintType,?> baseHints,
String characterSet,
ResultPointCallback resultPointCallback) {
this.activity = activity;
handlerInitLatch = new CountDownLatch(1);
hints = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
if (baseHints != null) {
hints.putAll(baseHints);
}
// The prefs can't change while the thread is running, so pick them up once here.
if (decodeFormats == null || decodeFormats.isEmpty()) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D_PRODUCT, true)) {
decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS);
}
if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D_INDUSTRIAL, true)) {
decodeFormats.addAll(DecodeFormatManager.INDUSTRIAL_FORMATS);
}
if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_QR, true)) {
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
}
if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_DATA_MATRIX, true)) {
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
}
if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_AZTEC, false)) {
decodeFormats.addAll(DecodeFormatManager.AZTEC_FORMATS);
}
if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_PDF417, false)) {
decodeFormats.addAll(DecodeFormatManager.PDF417_FORMATS);
}
}
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
if (characterSet != null) {
hints.put(DecodeHintType.CHARACTER_SET, characterSet);
}
hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
Log.i("DecodeThread", "Hints: " + hints);
}
Handler getHandler() {
try {
handlerInitLatch.await();
} catch (InterruptedException ie) {
// continue?
}
return handler;
}
@Override
public void run() {
Looper.prepare();
handler = new DecodeHandler(activity, hints);
handlerInitLatch.countDown();
Looper.loop();
}
}
/*
* Copyright (C) 2010 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 android.app.Activity;
import android.content.DialogInterface;
/**
* Simple listener used to exit the app in a few cases.
*
* @author Sean Owen
*/
public final class FinishListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
private final Activity activityToFinish;
public FinishListener(Activity activityToFinish) {
this.activityToFinish = activityToFinish;
}
@Override
public void onCancel(DialogInterface dialogInterface) {
run();
}
@Override
public void onClick(DialogInterface dialogInterface, int i) {
run();
}
private void run() {
activityToFinish.finish();
}
}
/*
* Copyright 2011 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 android.util.Log;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
/**
* Utility methods for retrieving content over HTTP using the more-supported {@code java.net} classes
* in Android.
*/
public final class HttpHelper {
private static final String TAG = HttpHelper.class.getSimpleName();
private static final Collection<String> REDIRECTOR_DOMAINS = new HashSet<String>(Arrays.asList(
"amzn.to", "bit.ly", "bitly.com", "fb.me", "goo.gl", "is.gd", "j.mp", "lnkd.in", "ow.ly",
"R.BEETAGG.COM", "r.beetagg.com", "SCN.BY", "su.pr", "t.co", "tinyurl.com", "tr.im"
));
private HttpHelper() {
}
public enum ContentType {
/** HTML-like content type, including HTML, XHTML, etc. */
HTML,
/** JSON content */
JSON,
/** XML */
XML,
/** Plain text content */
TEXT,
}
/**
* Downloads the entire resource instead of part.
*
* @param uri URI to retrieve
* @param type expected text-like MIME type of that content
* @return content as a {@code String}
* @throws IOException if the content can't be retrieved because of a bad URI, network problem, etc.
* @see #downloadViaHttp(String, HttpHelper.ContentType, int)
*/
public static CharSequence downloadViaHttp(String uri, ContentType type) throws IOException {
return downloadViaHttp(uri, type, Integer.MAX_VALUE);
}
/**
* @param uri URI to retrieve
* @param type expected text-like MIME type of that content
* @param maxChars approximate maximum characters to read from the source
* @return content as a {@code String}
* @throws IOException if the content can't be retrieved because of a bad URI, network problem, etc.
*/
public static CharSequence downloadViaHttp(String uri, ContentType type, int maxChars) throws IOException {
String contentTypes;
switch (type) {
case HTML:
contentTypes = "application/xhtml+xml,text/html,text/*,*/*";
break;
case JSON:
contentTypes = "application/json,text/*,*/*";
break;
case XML:
contentTypes = "application/xml,text/*,*/*";
break;
case TEXT:
default:
contentTypes = "text/*,*/*";
}
return downloadViaHttp(uri, contentTypes, maxChars);
}
private static CharSequence downloadViaHttp(String uri, String contentTypes, int maxChars) throws IOException {
int redirects = 0;
while (redirects < 5) {
URL url = new URL(uri);
HttpURLConnection connection = safelyOpenConnection(url);
connection.setInstanceFollowRedirects(true); // Won't work HTTP -> HTTPS or vice versa
connection.setRequestProperty("Accept", contentTypes);
connection.setRequestProperty("Accept-Charset", "utf-8,*");
connection.setRequestProperty("User-Agent", "ZXing (Android)");
try {
int responseCode = safelyConnect(connection);
switch (responseCode) {
case HttpURLConnection.HTTP_OK:
return consume(connection, maxChars);
case HttpURLConnection.HTTP_MOVED_TEMP:
String location = connection.getHeaderField("Location");
if (location != null) {
uri = location;
redirects++;
continue;
}
throw new IOException("No Location");
default:
throw new IOException("Bad HTTP response: " + responseCode);
}
} finally {
connection.disconnect();
}
}
throw new IOException("Too many redirects");
}
private static String getEncoding(URLConnection connection) {
String contentTypeHeader = connection.getHeaderField("Content-Type");
if (contentTypeHeader != null) {
int charsetStart = contentTypeHeader.indexOf("charset=");
if (charsetStart >= 0) {
return contentTypeHeader.substring(charsetStart + "charset=".length());
}
}
return "UTF-8";
}
private static CharSequence consume(URLConnection connection, int maxChars) throws IOException {
String encoding = getEncoding(connection);
StringBuilder out = new StringBuilder();
Reader in = null;
try {
in = new InputStreamReader(connection.getInputStream(), encoding);
char[] buffer = new char[1024];
int charsRead;
while (out.length() < maxChars && (charsRead = in.read(buffer)) > 0) {
out.append(buffer, 0, charsRead);
}
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
// continue
}catch (NullPointerException ioe) {
// continue
}
}
}
return out;
}
public static URI unredirect(URI uri) throws IOException {
if (!REDIRECTOR_DOMAINS.contains(uri.getHost())) {
return uri;
}
URL url = uri.toURL();
HttpURLConnection connection = safelyOpenConnection(url);
connection.setInstanceFollowRedirects(false);
connection.setDoInput(false);
connection.setRequestMethod("HEAD");
connection.setRequestProperty("User-Agent", "ZXing (Android)");
try {
int responseCode = safelyConnect(connection);
switch (responseCode) {
case HttpURLConnection.HTTP_MULT_CHOICE:
case HttpURLConnection.HTTP_MOVED_PERM:
case HttpURLConnection.HTTP_MOVED_TEMP:
case HttpURLConnection.HTTP_SEE_OTHER:
case 307: // No constant for 307 Temporary Redirect ?
String location = connection.getHeaderField("Location");
if (location != null) {
try {
return new URI(location);
} catch (URISyntaxException e) {
// nevermind
}
}
}
return uri;
} finally {
connection.disconnect();
}
}
private static HttpURLConnection safelyOpenConnection(URL url) throws IOException {
URLConnection conn;
try {
conn = url.openConnection();
} catch (NullPointerException npe) {
// Another strange bug in Android?
Log.w(TAG, "Bad URI? " + url);
throw new IOException(npe);
}
if (!(conn instanceof HttpURLConnection)) {
throw new IOException();
}
return (HttpURLConnection) conn;
}
private static int safelyConnect(HttpURLConnection connection) throws IOException {
try {
connection.connect();
} catch (NullPointerException e) {
// this is an Android bug: http://code.google.com/p/android/issues/detail?id=16895
throw new IOException(e);
}catch(IllegalArgumentException e) {
// this is an Android bug: http://code.google.com/p/android/issues/detail?id=16895
throw new IOException(e);
}catch(IndexOutOfBoundsException e) {
// this is an Android bug: http://code.google.com/p/android/issues/detail?id=16895
throw new IOException(e);
}catch(SecurityException e) {
// this is an Android bug: http://code.google.com/p/android/issues/detail?id=16895
throw new IOException(e);
}
try {
return connection.getResponseCode();
} catch (NullPointerException e) {
// this is maybe this Android bug: http://code.google.com/p/android/issues/detail?id=15554
throw new IOException(e);
}catch (StringIndexOutOfBoundsException e) {
// this is maybe this Android bug: http://code.google.com/p/android/issues/detail?id=15554
throw new IOException(e);
}catch (IllegalArgumentException e) {
// this is maybe this Android bug: http://code.google.com/p/android/issues/detail?id=15554
throw new IOException(e);
}
}
}
/*
* Copyright (C) 2010 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 android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.BatteryManager;
import android.util.Log;
/**
* Finishes an activity after a period of inactivity if the device is on battery power.
*/
final class InactivityTimer {
private static final String TAG = InactivityTimer.class.getSimpleName();
private static final long INACTIVITY_DELAY_MS = 5 * 60 * 1000L;
private final Activity activity;
private final BroadcastReceiver powerStatusReceiver;
private boolean registered;
private AsyncTask<Object,Object,Object> inactivityTask;
InactivityTimer(Activity activity) {
this.activity = activity;
powerStatusReceiver = new PowerStatusReceiver();
registered = false;
onActivity();
}
synchronized void onActivity() {
cancel();
inactivityTask = new InactivityAsyncTask();
inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public synchronized void onPause() {
cancel();
if (registered) {
activity.unregisterReceiver(powerStatusReceiver);
registered = false;
} else {
Log.w(TAG, "PowerStatusReceiver was never registered?");
}
}
public synchronized void onResume() {
if (registered) {
Log.w(TAG, "PowerStatusReceiver was already registered?");
} else {
activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
registered = true;
}
onActivity();
}
private synchronized void cancel() {
AsyncTask<?,?,?> task = inactivityTask;
if (task != null) {
task.cancel(true);
inactivityTask = null;
}
}
void shutdown() {
cancel();
}
private final class PowerStatusReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent){
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
// 0 indicates that we're on battery
boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;
if (onBatteryNow) {
InactivityTimer.this.onActivity();
} else {
InactivityTimer.this.cancel();
}
}
}
}
private final class InactivityAsyncTask extends AsyncTask<Object,Object,Object> {
@Override
protected Object doInBackground(Object... objects) {
try {
Thread.sleep(INACTIVITY_DELAY_MS);
Log.i(TAG, "Finishing activity due to inactivity");
activity.finish();
} catch (InterruptedException e) {
// continue without killing
}
return null;
}
}
}
/*
* Copyright (C) 2011 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;
enum IntentSource {
NATIVE_APP_INTENT,
PRODUCT_SEARCH_LINK,
ZXING_LINK,
NONE
}
/*
* 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;
/**
* 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() {
}
public static final class Scan {
/**
* Send this intent to open the Barcodes app in scanning mode, find a barcode, and return
* the results.
*/
public static final String ACTION = "com.google.zxing.client.android.SCAN";
/**
* By default, sending this will decode all barcodes that we understand. However it
* may be useful to limit scanning to certain formats. Use
* {@link android.content.Intent#putExtra(String, String)} with one of the values below.
*
* Setting this is effectively shorthand for setting explicit formats with {@link #FORMATS}.
* It is overridden by that setting.
*/
public static final String MODE = "SCAN_MODE";
/**
* Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get
* prices, reviews, etc. for products.
*/
public static final String PRODUCT_MODE = "PRODUCT_MODE";
/**
* Decode only 1D barcodes.
*/
public static final String ONE_D_MODE = "ONE_D_MODE";
/**
* Decode only QR codes.
*/
public static final String QR_CODE_MODE = "QR_CODE_MODE";
/**
* Decode only Data Matrix codes.
*/
public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE";
/**
* Decode only Aztec.
*/
public static final String AZTEC_MODE = "AZTEC_MODE";
/**
* Decode only PDF417.
*/
public static final String PDF417_MODE = "PDF417_MODE";
/**
* Comma-separated list of formats to scan for. The values must match the names of
* {@link com.google.zxing.BarcodeFormat}s, e.g. {@link com.google.zxing.BarcodeFormat#EAN_13}.
* Example: "EAN_13,EAN_8,QR_CODE". This overrides {@link #MODE}.
*/
public static final String FORMATS = "SCAN_FORMATS";
/**
* Optional parameter to specify the id of the camera from which to recognize barcodes.
* Overrides the default camera that would otherwise would have been selected.
* If provided, should be an int.
*/
public static final String CAMERA_ID = "SCAN_CAMERA_ID";
/**
* @see com.google.zxing.DecodeHintType#CHARACTER_SET
*/
public static final String CHARACTER_SET = "CHARACTER_SET";
/**
* Optional parameters to specify the width and height of the scanning rectangle in pixels.
* The app will try to honor these, but will clamp them to the size of the preview frame.
* You should specify both or neither, and pass the size as an int.
*/
public static final String WIDTH = "SCAN_WIDTH";
public static final String HEIGHT = "SCAN_HEIGHT";
/**
* Desired duration in milliseconds for which to pause after a successful scan before
* returning to the calling intent. Specified as a long, not an integer!
* For example: 1000L, not 1000.
*/
public static final String RESULT_DISPLAY_DURATION_MS = "RESULT_DISPLAY_DURATION_MS";
/**
* Prompt to show on-screen when scanning by intent. Specified as a {@link String}.
*/
public static final String PROMPT_MESSAGE = "PROMPT_MESSAGE";
/**
* If a barcode is found, Barcodes returns {@link android.app.Activity#RESULT_OK} to
* {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)}
* of the app which requested the scan via
* {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
* The barcodes contents can be retrieved with
* {@link android.content.Intent#getStringExtra(String)}.
* If the user presses Back, the result code will be {@link android.app.Activity#RESULT_CANCELED}.
*/
public static final String RESULT = "SCAN_RESULT";
/**
* Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_FORMAT}
* to determine which barcode format was found.
* See {@link com.google.zxing.BarcodeFormat} for possible values.
*/
public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";
/**
* Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_UPC_EAN_EXTENSION}
* to return the content of any UPC extension barcode that was also found. Only applicable
* to {@link com.google.zxing.BarcodeFormat#UPC_A} and {@link com.google.zxing.BarcodeFormat#EAN_13}
* formats.
*/
public static final String RESULT_UPC_EAN_EXTENSION = "SCAN_RESULT_UPC_EAN_EXTENSION";
/**
* Call {@link android.content.Intent#getByteArrayExtra(String)} with {@link #RESULT_BYTES}
* to get a {@code byte[]} of raw bytes in the barcode, if available.
*/
public static final String RESULT_BYTES = "SCAN_RESULT_BYTES";
/**
* Key for the value of {@link com.google.zxing.ResultMetadataType#ORIENTATION}, if available.
* Call {@link android.content.Intent#getIntArrayExtra(String)} with {@link #RESULT_ORIENTATION}.
*/
public static final String RESULT_ORIENTATION = "SCAN_RESULT_ORIENTATION";
/**
* Key for the value of {@link com.google.zxing.ResultMetadataType#ERROR_CORRECTION_LEVEL}, if available.
* Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_ERROR_CORRECTION_LEVEL}.
*/
public static final String RESULT_ERROR_CORRECTION_LEVEL = "SCAN_RESULT_ERROR_CORRECTION_LEVEL";
/**
* Prefix for keys that map to the values of {@link com.google.zxing.ResultMetadataType#BYTE_SEGMENTS},
* if available. The actual values will be set under a series of keys formed by adding 0, 1, 2, ...
* to this prefix. So the first byte segment is under key "SCAN_RESULT_BYTE_SEGMENTS_0" for example.
* Call {@link android.content.Intent#getByteArrayExtra(String)} with these keys.
*/
public static final String RESULT_BYTE_SEGMENTS_PREFIX = "SCAN_RESULT_BYTE_SEGMENTS_";
/**
* Setting this to false will not save scanned codes in the history. Specified as a {@code boolean}.
*/
public static final String SAVE_HISTORY = "SAVE_HISTORY";
private Scan() {
}
}
public static final class History {
public static final String ITEM_NUMBER = "ITEM_NUMBER";
private History() {
}
}
public static final class Encode {
/**
* Send this intent to encode a piece of data as a QR code and display it full screen, so
* that another person can scan the barcode from your screen.
*/
public static final String ACTION = "com.google.zxing.client.android.ENCODE";
/**
* The data to encode. Use {@link android.content.Intent#putExtra(String, String)} or
* {@link android.content.Intent#putExtra(String, android.os.Bundle)},
* depending on the type and format specified. Non-QR Code formats should
* just use a String here. For QR Code, see Contents for details.
*/
public static final String DATA = "ENCODE_DATA";
/**
* The type of data being supplied if the format is QR Code. Use
* {@link android.content.Intent#putExtra(String, String)} with one of {@link Contents.Type}.
*/
public static final String TYPE = "ENCODE_TYPE";
/**
* The barcode format to be displayed. If this isn't specified or is blank,
* it defaults to QR Code. Use {@link android.content.Intent#putExtra(String, String)}, where
* format is one of {@link com.google.zxing.BarcodeFormat}.
*/
public static final String FORMAT = "ENCODE_FORMAT";
/**
* Normally the contents of the barcode are displayed to the user in a TextView. Setting this
* boolean to false will hide that TextView, showing only the encode barcode.
*/
public static final String SHOW_CONTENTS = "ENCODE_SHOW_CONTENTS";
private Encode() {
}
}
public static final class SearchBookContents {
/**
* Use Google Book Search to search the contents of the book provided.
*/
public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS";
/**
* The book to search, identified by ISBN number.
*/
public static final String ISBN = "ISBN";
/**
* An optional field which is the text to search for.
*/
public static final String QUERY = "QUERY";
private SearchBookContents() {
}
}
public static final class WifiConnect {
/**
* Internal intent used to trigger connection to a wi-fi network.
*/
public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT";
/**
* The network to connect to, all the configuration provided here.
*/
public static final String SSID = "SSID";
/**
* The network to connect to, all the configuration provided here.
*/
public static final String TYPE = "TYPE";
/**
* The network to connect to, all the configuration provided here.
*/
public static final String PASSWORD = "PASSWORD";
private WifiConnect() {
}
}
public static final class Share {
/**
* Give the user a choice of items to encode as a barcode, then render it as a QR Code and
* display onscreen for a friend to scan with their phone.
*/
public static final String ACTION = "com.google.zxing.client.android.SHARE";
private Share() {
}
}
}
/*
* 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;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* 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 String DEFAULT_COUNTRY = "US";
private static final String DEFAULT_LANGUAGE = "en";
/**
* Locales (well, countries) where Google web search is available.
* These should be kept in sync with our translations.
*/
private static final Map<String,String> GOOGLE_COUNTRY_TLD;
static {
GOOGLE_COUNTRY_TLD = new HashMap<String, String>();
GOOGLE_COUNTRY_TLD.put("AR", "com.ar"); // ARGENTINA
GOOGLE_COUNTRY_TLD.put("AU", "com.au"); // AUSTRALIA
GOOGLE_COUNTRY_TLD.put("BR", "com.br"); // BRAZIL
GOOGLE_COUNTRY_TLD.put("BG", "bg"); // BULGARIA
GOOGLE_COUNTRY_TLD.put(Locale.CANADA.getCountry(), "ca");
GOOGLE_COUNTRY_TLD.put(Locale.CHINA.getCountry(), "cn");
GOOGLE_COUNTRY_TLD.put("CZ", "cz"); // CZECH REPUBLIC
GOOGLE_COUNTRY_TLD.put("DK", "dk"); // DENMARK
GOOGLE_COUNTRY_TLD.put("FI", "fi"); // FINLAND
GOOGLE_COUNTRY_TLD.put(Locale.FRANCE.getCountry(), "fr");
GOOGLE_COUNTRY_TLD.put(Locale.GERMANY.getCountry(), "de");
GOOGLE_COUNTRY_TLD.put("GR", "gr"); // GREECE
GOOGLE_COUNTRY_TLD.put("HU", "hu"); // HUNGARY
GOOGLE_COUNTRY_TLD.put("ID", "co.id"); // INDONESIA
GOOGLE_COUNTRY_TLD.put("IL", "co.il"); // ISRAEL
GOOGLE_COUNTRY_TLD.put(Locale.ITALY.getCountry(), "it");
GOOGLE_COUNTRY_TLD.put(Locale.JAPAN.getCountry(), "co.jp");
GOOGLE_COUNTRY_TLD.put(Locale.KOREA.getCountry(), "co.kr");
GOOGLE_COUNTRY_TLD.put("NL", "nl"); // NETHERLANDS
GOOGLE_COUNTRY_TLD.put("PL", "pl"); // POLAND
GOOGLE_COUNTRY_TLD.put("PT", "pt"); // PORTUGAL
GOOGLE_COUNTRY_TLD.put("RO", "ro"); // ROMANIA
GOOGLE_COUNTRY_TLD.put("RU", "ru"); // RUSSIA
GOOGLE_COUNTRY_TLD.put("SK", "sk"); // SLOVAK REPUBLIC
GOOGLE_COUNTRY_TLD.put("SI", "si"); // SLOVENIA
GOOGLE_COUNTRY_TLD.put("ES", "es"); // SPAIN
GOOGLE_COUNTRY_TLD.put("SE", "se"); // SWEDEN
GOOGLE_COUNTRY_TLD.put("CH", "ch"); // SWITZERLAND
GOOGLE_COUNTRY_TLD.put(Locale.TAIWAN.getCountry(), "tw");
GOOGLE_COUNTRY_TLD.put("TR", "com.tr"); // TURKEY
GOOGLE_COUNTRY_TLD.put("UA", "com.ua"); // UKRAINE
GOOGLE_COUNTRY_TLD.put(Locale.UK.getCountry(), "co.uk");
GOOGLE_COUNTRY_TLD.put(Locale.US.getCountry(), "com");
}
/**
* Google Product Search for mobile is available in fewer countries than web search. See here:
* http://support.google.com/merchants/bin/answer.py?hl=en-GB&answer=160619
*/
private static final Map<String,String> GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD;
static {
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD = new HashMap<String, String>();
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put("AU", "com.au"); // AUSTRALIA
//GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.CHINA.getCountry(), "cn");
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.FRANCE.getCountry(), "fr");
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.GERMANY.getCountry(), "de");
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.ITALY.getCountry(), "it");
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.JAPAN.getCountry(), "co.jp");
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put("NL", "nl"); // NETHERLANDS
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put("ES", "es"); // SPAIN
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put("CH", "ch"); // SWITZERLAND
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.UK.getCountry(), "co.uk");
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.US.getCountry(), "com");
}
/**
* Book search is offered everywhere that web search is available.
*/
private static final Map<String,String> GOOGLE_BOOK_SEARCH_COUNTRY_TLD = GOOGLE_COUNTRY_TLD;
private static final Collection<String> TRANSLATED_HELP_ASSET_LANGUAGES =
Arrays.asList("de", "en", "es", "fr", "it", "ja", "ko", "nl", "pt", "ru", "uk", "zh-rCN", "zh-rTW", "zh-rHK");
private LocaleManager() {}
/**
* @param context application's {@link Context}
* @return country-specific TLD suffix appropriate for the current default locale
* (e.g. "co.uk" for the United Kingdom)
*/
public static String getCountryTLD(Context context) {
return doGetTLD(GOOGLE_COUNTRY_TLD, context);
}
/**
* The same as above, but specifically for Google Product Search.
*
* @param context application's {@link Context}
* @return The top-level domain to use.
*/
public static String getProductSearchCountryTLD(Context context) {
return doGetTLD(GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD, context);
}
/**
* The same as above, but specifically for Google Book Search.
*
* @param context application's {@link Context}
* @return The top-level domain to use.
*/
public static String getBookSearchCountryTLD(Context context) {
return doGetTLD(GOOGLE_BOOK_SEARCH_COUNTRY_TLD, context);
}
/**
* Does a given URL point to Google Book Search, regardless of domain.
*
* @param url The address to check.
* @return True if this is a Book Search URL.
*/
public static boolean isBookSearchUrl(String url) {
return url.startsWith("http://google.com/books") || url.startsWith("http://books.google.");
}
private static String getSystemCountry() {
Locale locale = Locale.getDefault();
return locale == null ? DEFAULT_COUNTRY : locale.getCountry();
}
private static String getSystemLanguage() {
Locale locale = Locale.getDefault();
if (locale == null) {
return DEFAULT_LANGUAGE;
}
String language = locale.getLanguage();
// Special case Chinese
if (Locale.SIMPLIFIED_CHINESE.getLanguage().equals(language)) {
return language + "-r" + getSystemCountry();
}
return language;
}
public static String getTranslatedAssetLanguage() {
String language = getSystemLanguage();
return TRANSLATED_HELP_ASSET_LANGUAGES.contains(language) ? language : DEFAULT_LANGUAGE;
}
private static String doGetTLD(Map<String,String> map, Context context) {
String tld = map.get(getCountry(context));
return tld == null ? DEFAULT_TLD : tld;
}
public static String getCountry(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String countryOverride = prefs.getString(PreferencesActivity.KEY_SEARCH_COUNTRY, "-");
if (countryOverride != null && !countryOverride.isEmpty() && !"-".equals(countryOverride)) {
return countryOverride;
}
return getSystemCountry();
}
}
/*
* 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;
import android.app.Activity;
import android.os.Bundle;
/**
* The main settings activity.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen
*/
public final class PreferencesActivity extends Activity {
public static final String KEY_DECODE_1D_PRODUCT = "preferences_decode_1D_product";
public static final String KEY_DECODE_1D_INDUSTRIAL = "preferences_decode_1D_industrial";
public static final String KEY_DECODE_QR = "preferences_decode_QR";
public static final String KEY_DECODE_DATA_MATRIX = "preferences_decode_Data_Matrix";
public static final String KEY_DECODE_AZTEC = "preferences_decode_Aztec";
public static final String KEY_DECODE_PDF417 = "preferences_decode_PDF417";
public static final String KEY_CUSTOM_PRODUCT_SEARCH = "preferences_custom_product_search";
public static final String KEY_PLAY_BEEP = "preferences_play_beep";
public static final String KEY_VIBRATE = "preferences_vibrate";
public static final String KEY_COPY_TO_CLIPBOARD = "preferences_copy_to_clipboard";
public static final String KEY_FRONT_LIGHT_MODE = "preferences_front_light_mode";
public static final String KEY_BULK_MODE = "preferences_bulk_mode";
public static final String KEY_REMEMBER_DUPLICATES = "preferences_remember_duplicates";
public static final String KEY_ENABLE_HISTORY = "preferences_history";
public static final String KEY_SUPPLEMENTAL = "preferences_supplemental";
public static final String KEY_AUTO_FOCUS = "preferences_auto_focus";
public static final String KEY_INVERT_SCAN = "preferences_invert_scan";
public static final String KEY_SEARCH_COUNTRY = "preferences_search_country";
public static final String KEY_DISABLE_AUTO_ORIENTATION = "preferences_orientation";
public static final String KEY_DISABLE_CONTINUOUS_FOCUS = "preferences_disable_continuous_focus";
public static final String KEY_DISABLE_EXPOSURE = "preferences_disable_exposure";
public static final String KEY_DISABLE_METERING = "preferences_disable_metering";
public static final String KEY_DISABLE_BARCODE_SCENE_MODE = "preferences_disable_barcode_scene_mode";
public static final String KEY_AUTO_OPEN_WEB = "preferences_auto_open_web";
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
getFragmentManager().beginTransaction().replace(android.R.id.content, new PreferencesFragment()).commit();
}
// Apparently this will be necessary when targeting API 19+:
/*
@Override
protected boolean isValidFragment(String fragmentName) {
return true;
}
*/
}
/*
* Copyright (C) 2013 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 android.app.AlertDialog;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import com.senthink.ebike_administrer_tool.R;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
public final class PreferencesFragment
extends PreferenceFragment
implements SharedPreferences.OnSharedPreferenceChangeListener {
private CheckBoxPreference[] checkBoxPrefs;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.preferences);
PreferenceScreen preferences = getPreferenceScreen();
preferences.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
checkBoxPrefs = findDecodePrefs(preferences,
PreferencesActivity.KEY_DECODE_1D_PRODUCT,
PreferencesActivity.KEY_DECODE_1D_INDUSTRIAL,
PreferencesActivity.KEY_DECODE_QR,
PreferencesActivity.KEY_DECODE_DATA_MATRIX,
PreferencesActivity.KEY_DECODE_AZTEC,
PreferencesActivity.KEY_DECODE_PDF417);
disableLastCheckedPref();
EditTextPreference customProductSearch = (EditTextPreference)
preferences.findPreference(PreferencesActivity.KEY_CUSTOM_PRODUCT_SEARCH);
customProductSearch.setOnPreferenceChangeListener(new CustomSearchURLValidator());
}
private static CheckBoxPreference[] findDecodePrefs(PreferenceScreen preferences, String... keys) {
CheckBoxPreference[] prefs = new CheckBoxPreference[keys.length];
for (int i = 0; i < keys.length; i++) {
prefs[i] = (CheckBoxPreference) preferences.findPreference(keys[i]);
}
return prefs;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
disableLastCheckedPref();
}
private void disableLastCheckedPref() {
Collection<CheckBoxPreference> checked = new ArrayList<CheckBoxPreference>(checkBoxPrefs.length);
for (CheckBoxPreference pref : checkBoxPrefs) {
if (pref.isChecked()) {
checked.add(pref);
}
}
boolean disable = checked.size() <= 1;
for (CheckBoxPreference pref : checkBoxPrefs) {
pref.setEnabled(!(disable && checked.contains(pref)));
}
}
private class CustomSearchURLValidator implements Preference.OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (!isValid(newValue)) {
AlertDialog.Builder builder =
new AlertDialog.Builder(PreferencesFragment.this.getActivity());
builder.setTitle("错误");
builder.setMessage("值无效");
builder.setCancelable(true);
builder.show();
return false;
}
return true;
}
private boolean isValid(Object newValue) {
// Allow empty/null value
if (newValue == null) {
return true;
}
String valueString = newValue.toString();
if (valueString.isEmpty()) {
return true;
}
// Before validating, remove custom placeholders, which will not
// be considered valid parts of the URL in some locations:
// Blank %t and %s:
valueString = valueString.replaceAll("%[st]", "");
// Blank %f but not if followed by digit or a-f as it may be a hex sequence
valueString = valueString.replaceAll("%f(?![0-9a-f])", "");
// Require a scheme otherwise:
try {
URI uri = new URI(valueString);
return uri.getScheme() != null;
} catch (URISyntaxException use) {
return false;
}
}
}
}
/*
* Copyright (C) 2012 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 android.net.Uri;
import com.google.zxing.Result;
import com.google.zxing.client.android.result.ResultHandler;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/**
* Manages functionality related to responding to requests to scan from an HTTP link in a web page.
* See <a href="http://github.com/zxing/zxing/wiki/ScanningFromWebPages">ScanningFromWebPages</a>.
*
* @author Sean Owen
*/
final class ScanFromWebPageManager {
private static final CharSequence CODE_PLACEHOLDER = "{CODE}";
private static final CharSequence RAW_CODE_PLACEHOLDER = "{RAWCODE}";
private static final CharSequence META_PLACEHOLDER = "{META}";
private static final CharSequence FORMAT_PLACEHOLDER = "{FORMAT}";
private static final CharSequence TYPE_PLACEHOLDER = "{TYPE}";
private static final String RETURN_URL_PARAM = "ret";
private static final String RAW_PARAM = "raw";
private final String returnUrlTemplate;
private final boolean returnRaw;
ScanFromWebPageManager(Uri inputUri) {
returnUrlTemplate = inputUri.getQueryParameter(RETURN_URL_PARAM);
returnRaw = inputUri.getQueryParameter(RAW_PARAM) != null;
}
boolean isScanFromWebPage() {
return returnUrlTemplate != null;
}
String buildReplyURL(Result rawResult, ResultHandler resultHandler) {
String result = returnUrlTemplate;
result = replace(CODE_PLACEHOLDER,
returnRaw ? rawResult.getText() : resultHandler.getDisplayContents(), result);
result = replace(RAW_CODE_PLACEHOLDER, rawResult.getText(), result);
result = replace(FORMAT_PLACEHOLDER, rawResult.getBarcodeFormat().toString(), result);
result = replace(TYPE_PLACEHOLDER, resultHandler.getType().toString(), result);
result = replace(META_PLACEHOLDER, String.valueOf(rawResult.getResultMetadata()), result);
return result;
}
private static String replace(CharSequence placeholder, CharSequence with, String pattern) {
CharSequence escapedWith = with == null ? "" : with;
try {
escapedWith = URLEncoder.encode(escapedWith.toString(), "UTF-8");
} catch (UnsupportedEncodingException e) {
// can't happen; UTF-8 is always supported. Continue, I guess, without encoding
}
return pattern.replace(placeholder, escapedWith);
}
}
/*
* Copyright (C) 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.ResultPoint;
import com.google.zxing.ResultPointCallback;
final class ViewfinderResultPointCallback implements ResultPointCallback {
private final ViewfinderView viewfinderView;
ViewfinderResultPointCallback(ViewfinderView viewfinderView) {
this.viewfinderView = viewfinderView;
}
@Override
public void foundPossibleResultPoint(ResultPoint point) {
viewfinderView.addPossibleResultPoint(point);
}
}
/*
* 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;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import com.google.zxing.ResultPoint;
import com.senthink.ebike_administrer_tool.R;
import com.google.zxing.client.android.camera.CameraManager;
import java.util.ArrayList;
import java.util.List;
/**
* 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 = 80L;
private static final int CURRENT_POINT_OPACITY = 0xA0;
private static final int MAX_RESULT_POINTS = 20;
private static final int POINT_SIZE = 6;
private CameraManager cameraManager;
private final Paint paint;
private Bitmap resultBitmap;
private final int maskColor;
private final int resultColor;
private final int laserColor;
private final int resultPointColor;
private int scannerAlpha;
private List<ResultPoint> possibleResultPoints;
private List<ResultPoint> lastPossibleResultPoints;
// 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().
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Resources resources = getResources();
maskColor = resources.getColor(R.color.viewfinder_mask);
resultColor = resources.getColor(R.color.result_view);
laserColor = resources.getColor(R.color.viewfinder_laser);
resultPointColor = resources.getColor(R.color.possible_result_points);
scannerAlpha = 0;
possibleResultPoints = new ArrayList<ResultPoint>(5);
lastPossibleResultPoints = null;
}
public void setCameraManager(CameraManager cameraManager) {
this.cameraManager = cameraManager;
}
@SuppressLint("DrawAllocation")
@Override
public void onDraw(Canvas canvas) {
if (cameraManager == null) {
return; // not ready yet, early draw before done configuring
}
Rect frame = cameraManager.getFramingRect();
Rect previewFrame = cameraManager.getFramingRectInPreview();
if (frame == null || previewFrame == null) {
return;
}
int width = canvas.getWidth();
int height = canvas.getHeight();
// Draw the exterior (i.e. outside the framing rect) darkened
paint.setColor(resultBitmap != null ? resultColor : maskColor);
canvas.drawRect(0, 0, width, frame.top, paint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
canvas.drawRect(0, frame.bottom + 1, width, height, paint);
if (resultBitmap != null) {
// Draw the opaque result bitmap over the scanning rectangle
paint.setAlpha(CURRENT_POINT_OPACITY);
canvas.drawBitmap(resultBitmap, null, frame, paint);
} else {
drawFrameBounds(canvas, frame);
// Draw a red "laser scanner" line through the middle to show decoding is active
paint.setColor(laserColor);
paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
int middle = frame.height() / 2 + frame.top;
canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
float scaleX = frame.width() / (float) previewFrame.width();
float scaleY = frame.height() / (float) previewFrame.height();
List<ResultPoint> currentPossible = possibleResultPoints;
List<ResultPoint> currentLast = lastPossibleResultPoints;
int frameLeft = frame.left;
int frameTop = frame.top;
if (currentPossible.isEmpty()) {
lastPossibleResultPoints = null;
} else {
possibleResultPoints = new ArrayList<ResultPoint>(5);
lastPossibleResultPoints = currentPossible;
paint.setAlpha(CURRENT_POINT_OPACITY);
paint.setColor(resultPointColor);
synchronized (currentPossible) {
for (ResultPoint point : currentPossible) {
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
POINT_SIZE, paint);
}
}
}
if (currentLast != null) {
paint.setAlpha(CURRENT_POINT_OPACITY / 2);
paint.setColor(resultPointColor);
synchronized (currentLast) {
float radius = POINT_SIZE / 2.0f;
for (ResultPoint point : currentLast) {
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
radius, paint);
}
}
}
// Request another update at the animation interval, but only repaint the laser line,
// not the entire viewfinder mask.
postInvalidateDelayed(ANIMATION_DELAY,
frame.left - POINT_SIZE,
frame.top - POINT_SIZE,
frame.right + POINT_SIZE,
frame.bottom + POINT_SIZE);
}
}
public void drawViewfinder() {
Bitmap resultBitmap = this.resultBitmap;
this.resultBitmap = null;
if (resultBitmap != null) {
resultBitmap.recycle();
}
invalidate();
}
/**
* Draw a bitmap with the result points highlighted instead of the live scanning display.
*
* @param barcode An image of the decoded barcode.
*/
public void drawResultBitmap(Bitmap barcode) {
resultBitmap = barcode;
invalidate();
}
public void addPossibleResultPoint(ResultPoint point) {
List<ResultPoint> points = possibleResultPoints;
synchronized (points) {
points.add(point);
int size = points.size();
if (size > MAX_RESULT_POINTS) {
// trim it
points.subList(0, size - MAX_RESULT_POINTS / 2).clear();
}
}
}
/**
* 绘制取景框边框
*
* @param canvas
* @param frame
*/
private void drawFrameBounds(Canvas canvas, Rect frame) {
paint.setColor(Color.WHITE);
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(frame, paint);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
int corWidth = 15;
int corLength = 45;
// 左上角
canvas.drawRect(frame.left - corWidth, frame.top, frame.left, frame.top
+ corLength, paint);
canvas.drawRect(frame.left - corWidth, frame.top - corWidth, frame.left
+ corLength, frame.top, paint);
// 右上角
canvas.drawRect(frame.right, frame.top, frame.right + corWidth,
frame.top + corLength, paint);
canvas.drawRect(frame.right - corLength, frame.top - corWidth,
frame.right + corWidth, frame.top, paint);
// 左下角
canvas.drawRect(frame.left - corWidth, frame.bottom - corLength,
frame.left, frame.bottom, paint);
canvas.drawRect(frame.left - corWidth, frame.bottom, frame.left
+ corLength, frame.bottom + corWidth, paint);
// 右下角
canvas.drawRect(frame.right, frame.bottom - corLength, frame.right
+ corWidth, frame.bottom, paint);
canvas.drawRect(frame.right - corLength, frame.bottom, frame.right
+ corWidth, frame.bottom + corWidth, paint);
}
}
/*
* Copyright (C) 2012 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.camera;
import android.content.Context;
import android.content.SharedPreferences;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.Log;
import com.google.zxing.client.android.PreferencesActivity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.RejectedExecutionException;
final class AutoFocusManager implements Camera.AutoFocusCallback {
private static final String TAG = AutoFocusManager.class.getSimpleName();
private static final long AUTO_FOCUS_INTERVAL_MS = 1000L;
private static final Collection<String> FOCUS_MODES_CALLING_AF;
static {
FOCUS_MODES_CALLING_AF = new ArrayList<String>(2);
FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);
FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);
}
private boolean stopped;
private boolean focusing;
private final boolean useAutoFocus;
private final Camera camera;
private AsyncTask<?,?,?> outstandingTask;
AutoFocusManager(Context context, Camera camera) {
this.camera = camera;
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
String currentFocusMode = camera.getParameters().getFocusMode();
useAutoFocus =
sharedPrefs.getBoolean(PreferencesActivity.KEY_AUTO_FOCUS, true) &&
FOCUS_MODES_CALLING_AF.contains(currentFocusMode);
Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);
start();
}
@Override
public synchronized void onAutoFocus(boolean success, Camera theCamera) {
focusing = false;
autoFocusAgainLater();
}
private synchronized void autoFocusAgainLater() {
if (!stopped && outstandingTask == null) {
AutoFocusTask newTask = new AutoFocusTask();
try {
newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
outstandingTask = newTask;
} catch (RejectedExecutionException ree) {
Log.w(TAG, "Could not request auto focus", ree);
}
}
}
synchronized void start() {
if (useAutoFocus) {
outstandingTask = null;
if (!stopped && !focusing) {
try {
camera.autoFocus(this);
focusing = true;
} catch (RuntimeException re) {
// Have heard RuntimeException reported in Android 4.0.x+; continue?
Log.w(TAG, "Unexpected exception while focusing", re);
// Try again later to keep cycle going
autoFocusAgainLater();
}
}
}
}
private synchronized void cancelOutstandingTask() {
if (outstandingTask != null) {
if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {
outstandingTask.cancel(true);
}
outstandingTask = null;
}
}
synchronized void stop() {
stopped = true;
if (useAutoFocus) {
cancelOutstandingTask();
// Doesn't hurt to call this even if not focusing
try {
camera.cancelAutoFocus();
} catch (RuntimeException re) {
// Have heard RuntimeException reported in Android 4.0.x+; continue?
Log.w(TAG, "Unexpected exception while cancelling focusing", re);
}
}
}
private final class AutoFocusTask extends AsyncTask<Object,Object,Object> {
@Override
protected Object doInBackground(Object... voids) {
try {
Thread.sleep(AUTO_FOCUS_INTERVAL_MS);
} catch (InterruptedException e) {
// continue
}
start();
return null;
}
}
}
/*
* Copyright (C) 2010 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.camera;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Point;
import android.hardware.Camera;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import com.google.zxing.client.android.PreferencesActivity;
import com.google.zxing.client.android.camera.open.CameraFacing;
import com.google.zxing.client.android.camera.open.OpenCamera;
/**
* A class which deals with reading, parsing, and setting the camera parameters which are used to
* configure the camera hardware.
*/
final class CameraConfigurationManager {
private static final String TAG = "CameraConfiguration";
private final Context context;
private int cwNeededRotation;
private int cwRotationFromDisplayToCamera;
private Point screenResolution;
private Point cameraResolution;
private Point bestPreviewSize;
private Point previewSizeOnScreen;
CameraConfigurationManager(Context context) {
this.context = context;
}
/**
* Reads, one time, values from the camera that are needed by the app.
*/
void initFromCameraParameters(OpenCamera camera) {
Camera.Parameters parameters = camera.getCamera().getParameters();
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
int displayRotation = display.getRotation();
int cwRotationFromNaturalToDisplay;
switch (displayRotation) {
// 各个角度的二维码
case Surface.ROTATION_0:
cwRotationFromNaturalToDisplay = 0;
break;
case Surface.ROTATION_90:
cwRotationFromNaturalToDisplay = 90;
break;
case Surface.ROTATION_180:
cwRotationFromNaturalToDisplay = 180;
break;
case Surface.ROTATION_270:
cwRotationFromNaturalToDisplay = 270;
break;
default:
// Have seen this return incorrect values like -90
if (displayRotation % 90 == 0) {
cwRotationFromNaturalToDisplay = (360 + displayRotation) % 360;
} else {
throw new IllegalArgumentException("Bad rotation: " + displayRotation);
}
}
Log.i(TAG, "Display at: " + cwRotationFromNaturalToDisplay);
int cwRotationFromNaturalToCamera = camera.getOrientation();
Log.i(TAG, "Camera at: " + cwRotationFromNaturalToCamera);
// Still not 100% sure about this. But acts like we need to flip this:
if (camera.getFacing() == CameraFacing.FRONT) {
cwRotationFromNaturalToCamera = (360 - cwRotationFromNaturalToCamera) % 360;
Log.i(TAG, "Front camera overriden to: " + cwRotationFromNaturalToCamera);
}
/*
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String overrideRotationString;
if (camera.getFacing() == CameraFacing.FRONT) {
overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION_FRONT, null);
} else {
overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION, null);
}
if (overrideRotationString != null && !"-".equals(overrideRotationString)) {
Log.i(TAG, "Overriding camera manually to " + overrideRotationString);
cwRotationFromNaturalToCamera = Integer.parseInt(overrideRotationString);
}
*/
cwRotationFromDisplayToCamera =
(360 + cwRotationFromNaturalToCamera - cwRotationFromNaturalToDisplay) % 360;
Log.i(TAG, "Final display orientation: " + cwRotationFromDisplayToCamera);
if (camera.getFacing() == CameraFacing.FRONT) {
Log.i(TAG, "Compensating rotation for front camera");
cwNeededRotation = (360 - cwRotationFromDisplayToCamera) % 360;
} else {
cwNeededRotation = cwRotationFromDisplayToCamera;
}
Log.i(TAG, "Clockwise rotation from display to camera: " + cwNeededRotation);
Point theScreenResolution = new Point();
display.getSize(theScreenResolution);
screenResolution = theScreenResolution;
Log.i(TAG, "Screen resolution in current orientation: " + screenResolution);
cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
Log.i(TAG, "Camera resolution: " + cameraResolution);
bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
Log.i(TAG, "Best available preview size: " + bestPreviewSize);
boolean isScreenPortrait = screenResolution.x < screenResolution.y;
boolean isPreviewSizePortrait = bestPreviewSize.x < bestPreviewSize.y;
if (isScreenPortrait == isPreviewSizePortrait) {
previewSizeOnScreen = bestPreviewSize;
} else {
previewSizeOnScreen = new Point(bestPreviewSize.y, bestPreviewSize.x);
}
Log.i(TAG, "Preview size on screen: " + previewSizeOnScreen);
}
void setDesiredCameraParameters(OpenCamera camera, boolean safeMode) {
Camera theCamera = camera.getCamera();
Camera.Parameters parameters = theCamera.getParameters();
if (parameters == null) {
Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration.");
return;
}
Log.i(TAG, "Initial camera parameters: " + parameters.flatten());
if (safeMode) {
Log.w(TAG, "In camera config safe mode -- most settings will not be honored");
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
initializeTorch(parameters, prefs, safeMode);
CameraConfigurationUtils.setFocus(
parameters,
prefs.getBoolean(PreferencesActivity.KEY_AUTO_FOCUS, true),
prefs.getBoolean(PreferencesActivity.KEY_DISABLE_CONTINUOUS_FOCUS, true),
safeMode);
if (!safeMode) {
if (prefs.getBoolean(PreferencesActivity.KEY_INVERT_SCAN, false)) {
CameraConfigurationUtils.setInvertColor(parameters);
}
if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_BARCODE_SCENE_MODE, true)) {
CameraConfigurationUtils.setBarcodeSceneMode(parameters);
}
if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_METERING, true)) {
CameraConfigurationUtils.setVideoStabilization(parameters);
CameraConfigurationUtils.setFocusArea(parameters);
CameraConfigurationUtils.setMetering(parameters);
}
}
parameters.setPreviewSize(bestPreviewSize.x, bestPreviewSize.y);
theCamera.setDisplayOrientation(90);
theCamera.setParameters(parameters);
theCamera.setDisplayOrientation(cwRotationFromDisplayToCamera);
Camera.Parameters afterParameters = theCamera.getParameters();
Camera.Size afterSize = afterParameters.getPreviewSize();
if (afterSize != null && (bestPreviewSize.x != afterSize.width || bestPreviewSize.y != afterSize.height)) {
Log.w(TAG, "Camera said it supported preview size " + bestPreviewSize.x + 'x' + bestPreviewSize.y +
", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height);
bestPreviewSize.x = afterSize.width;
bestPreviewSize.y = afterSize.height;
}
}
Point getBestPreviewSize() {
return bestPreviewSize;
}
Point getPreviewSizeOnScreen() {
return previewSizeOnScreen;
}
Point getCameraResolution() {
return cameraResolution;
}
Point getScreenResolution() {
return screenResolution;
}
int getCWNeededRotation() {
return cwNeededRotation;
}
boolean getTorchState(Camera camera) {
if (camera != null) {
Camera.Parameters parameters = camera.getParameters();
if (parameters != null) {
String flashMode = camera.getParameters().getFlashMode();
return flashMode != null &&
(Camera.Parameters.FLASH_MODE_ON.equals(flashMode) ||
Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode));
}
}
return false;
}
void setTorch(Camera camera, boolean newSetting) {
Camera.Parameters parameters = camera.getParameters();
doSetTorch(parameters, newSetting, false);
camera.setParameters(parameters);
}
private void initializeTorch(Camera.Parameters parameters, SharedPreferences prefs, boolean safeMode) {
boolean currentSetting = FrontLightMode.readPref(prefs) == FrontLightMode.ON;
doSetTorch(parameters, currentSetting, safeMode);
}
private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode) {
CameraConfigurationUtils.setTorch(parameters, newSetting);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (!safeMode && !prefs.getBoolean(PreferencesActivity.KEY_DISABLE_EXPOSURE, true)) {
CameraConfigurationUtils.setBestExposure(parameters, newSetting);
}
}
}
/*
* Copyright (C) 2014 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.camera;
import android.annotation.TargetApi;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.os.Build;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
/**
* Utility methods for configuring the Android camera.
*
* @author Sean Owen
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
public final class CameraConfigurationUtils {
private static final String TAG = "CameraConfiguration";
private static final Pattern SEMICOLON = Pattern.compile(";");
private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen
private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;
private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;
private static final double MAX_ASPECT_DISTORTION = 0.15;
private static final int MIN_FPS = 10;
private static final int MAX_FPS = 20;
private static final int AREA_PER_1000 = 400;
private CameraConfigurationUtils() {
}
public static void setFocus(Camera.Parameters parameters,
boolean autoFocus,
boolean disableContinuous,
boolean safeMode) {
List<String> supportedFocusModes = parameters.getSupportedFocusModes();
String focusMode = null;
if (autoFocus) {
if (safeMode || disableContinuous) {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_AUTO);
} else {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
Camera.Parameters.FOCUS_MODE_AUTO);
}
}
// Maybe selected auto-focus but not available, so fall through here:
if (!safeMode && focusMode == null) {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_MACRO,
Camera.Parameters.FOCUS_MODE_EDOF);
}
if (focusMode != null) {
if (focusMode.equals(parameters.getFocusMode())) {
Log.i(TAG, "Focus mode already set to " + focusMode);
} else {
parameters.setFocusMode(focusMode);
}
}
}
public static void setTorch(Camera.Parameters parameters, boolean on) {
List<String> supportedFlashModes = parameters.getSupportedFlashModes();
String flashMode;
if (on) {
flashMode = findSettableValue("flash mode",
supportedFlashModes,
Camera.Parameters.FLASH_MODE_TORCH,
Camera.Parameters.FLASH_MODE_ON);
} else {
flashMode = findSettableValue("flash mode",
supportedFlashModes,
Camera.Parameters.FLASH_MODE_OFF);
}
if (flashMode != null) {
if (flashMode.equals(parameters.getFlashMode())) {
Log.i(TAG, "Flash mode already set to " + flashMode);
} else {
Log.i(TAG, "Setting flash mode to " + flashMode);
parameters.setFlashMode(flashMode);
}
}
}
public static void setBestExposure(Camera.Parameters parameters, boolean lightOn) {
int minExposure = parameters.getMinExposureCompensation();
int maxExposure = parameters.getMaxExposureCompensation();
float step = parameters.getExposureCompensationStep();
if ((minExposure != 0 || maxExposure != 0) && step > 0.0f) {
// Set low when light is on
float targetCompensation = lightOn ? MIN_EXPOSURE_COMPENSATION : MAX_EXPOSURE_COMPENSATION;
int compensationSteps = Math.round(targetCompensation / step);
float actualCompensation = step * compensationSteps;
// Clamp value:
compensationSteps = Math.max(Math.min(compensationSteps, maxExposure), minExposure);
if (parameters.getExposureCompensation() == compensationSteps) {
Log.i(TAG, "Exposure compensation already set to " + compensationSteps + " / " + actualCompensation);
} else {
Log.i(TAG, "Setting exposure compensation to " + compensationSteps + " / " + actualCompensation);
parameters.setExposureCompensation(compensationSteps);
}
} else {
Log.i(TAG, "Camera does not support exposure compensation");
}
}
public static void setBestPreviewFPS(Camera.Parameters parameters) {
setBestPreviewFPS(parameters, MIN_FPS, MAX_FPS);
}
public static void setBestPreviewFPS(Camera.Parameters parameters, int minFPS, int maxFPS) {
List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange();
Log.i(TAG, "Supported FPS ranges: " + toString(supportedPreviewFpsRanges));
if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) {
int[] suitableFPSRange = null;
for (int[] fpsRange : supportedPreviewFpsRanges) {
int thisMin = fpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
int thisMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
if (thisMin >= minFPS * 1000 && thisMax <= maxFPS * 1000) {
suitableFPSRange = fpsRange;
break;
}
}
if (suitableFPSRange == null) {
Log.i(TAG, "No suitable FPS range?");
} else {
int[] currentFpsRange = new int[2];
parameters.getPreviewFpsRange(currentFpsRange);
if (Arrays.equals(currentFpsRange, suitableFPSRange)) {
Log.i(TAG, "FPS range already set to " + Arrays.toString(suitableFPSRange));
} else {
Log.i(TAG, "Setting FPS range to " + Arrays.toString(suitableFPSRange));
parameters.setPreviewFpsRange(suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
}
}
public static void setFocusArea(Camera.Parameters parameters) {
if (parameters.getMaxNumFocusAreas() > 0) {
Log.i(TAG, "Old focus areas: " + toString(parameters.getFocusAreas()));
List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
Log.i(TAG, "Setting focus area to : " + toString(middleArea));
parameters.setFocusAreas(middleArea);
} else {
Log.i(TAG, "Device does not support focus areas");
}
}
public static void setMetering(Camera.Parameters parameters) {
if (parameters.getMaxNumMeteringAreas() > 0) {
Log.i(TAG, "Old metering areas: " + parameters.getMeteringAreas());
List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
Log.i(TAG, "Setting metering area to : " + toString(middleArea));
parameters.setMeteringAreas(middleArea);
} else {
Log.i(TAG, "Device does not support metering areas");
}
}
private static List<Camera.Area> buildMiddleArea(int areaPer1000) {
return Collections.singletonList(
new Camera.Area(new Rect(-areaPer1000, -areaPer1000, areaPer1000, areaPer1000), 1));
}
public static void setVideoStabilization(Camera.Parameters parameters) {
if (parameters.isVideoStabilizationSupported()) {
if (parameters.getVideoStabilization()) {
Log.i(TAG, "Video stabilization already enabled");
} else {
Log.i(TAG, "Enabling video stabilization...");
parameters.setVideoStabilization(true);
}
} else {
Log.i(TAG, "This device does not support video stabilization");
}
}
public static void setBarcodeSceneMode(Camera.Parameters parameters) {
if (Camera.Parameters.SCENE_MODE_BARCODE.equals(parameters.getSceneMode())) {
Log.i(TAG, "Barcode scene mode already set");
return;
}
String sceneMode = findSettableValue("scene mode",
parameters.getSupportedSceneModes(),
Camera.Parameters.SCENE_MODE_BARCODE);
if (sceneMode != null) {
parameters.setSceneMode(sceneMode);
}
}
public static void setZoom(Camera.Parameters parameters, double targetZoomRatio) {
if (parameters.isZoomSupported()) {
Integer zoom = indexOfClosestZoom(parameters, targetZoomRatio);
if (zoom == null) {
return;
}
if (parameters.getZoom() == zoom) {
Log.i(TAG, "Zoom is already set to " + zoom);
} else {
Log.i(TAG, "Setting zoom to " + zoom);
parameters.setZoom(zoom);
}
} else {
Log.i(TAG, "Zoom is not supported");
}
}
private static Integer indexOfClosestZoom(Camera.Parameters parameters, double targetZoomRatio) {
List<Integer> ratios = parameters.getZoomRatios();
Log.i(TAG, "Zoom ratios: " + ratios);
int maxZoom = parameters.getMaxZoom();
if (ratios == null || ratios.isEmpty() || ratios.size() != maxZoom + 1) {
Log.w(TAG, "Invalid zoom ratios!");
return null;
}
double target100 = 100.0 * targetZoomRatio;
double smallestDiff = Double.POSITIVE_INFINITY;
int closestIndex = 0;
for (int i = 0; i < ratios.size(); i++) {
double diff = Math.abs(ratios.get(i) - target100);
if (diff < smallestDiff) {
smallestDiff = diff;
closestIndex = i;
}
}
Log.i(TAG, "Chose zoom ratio of " + (ratios.get(closestIndex) / 100.0));
return closestIndex;
}
public static void setInvertColor(Camera.Parameters parameters) {
if (Camera.Parameters.EFFECT_NEGATIVE.equals(parameters.getColorEffect())) {
Log.i(TAG, "Negative effect already set");
return;
}
String colorMode = findSettableValue("color effect",
parameters.getSupportedColorEffects(),
Camera.Parameters.EFFECT_NEGATIVE);
if (colorMode != null) {
parameters.setColorEffect(colorMode);
}
}
public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {
List<Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
if (rawSupportedSizes == null) {
Log.w(TAG, "Device returned no supported preview sizes; using default");
Camera.Size defaultSize = parameters.getPreviewSize();
if (defaultSize == null) {
throw new IllegalStateException("Parameters contained no preview size!");
}
return new Point(defaultSize.width, defaultSize.height);
}
// Sort by size, descending
List<Size> supportedPreviewSizes = new ArrayList<Size>(rawSupportedSizes);
Collections.sort(supportedPreviewSizes, new Comparator<Size>() {
@Override
public int compare(Camera.Size a, Camera.Size b) {
int aPixels = a.height * a.width;
int bPixels = b.height * b.width;
if (bPixels < aPixels) {
return -1;
}
if (bPixels > aPixels) {
return 1;
}
return 0;
}
});
if (Log.isLoggable(TAG, Log.INFO)) {
StringBuilder previewSizesString = new StringBuilder();
for (Camera.Size supportedPreviewSize : supportedPreviewSizes) {
previewSizesString.append(supportedPreviewSize.width).append('x')
.append(supportedPreviewSize.height).append(' ');
}
Log.i(TAG, "Supported preview sizes: " + previewSizesString);
}
double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;
// Remove sizes that are unsuitable
Iterator<Size> it = supportedPreviewSizes.iterator();
while (it.hasNext()) {
Camera.Size supportedPreviewSize = it.next();
int realWidth = supportedPreviewSize.width;
int realHeight = supportedPreviewSize.height;
if (realWidth * realHeight < MIN_PREVIEW_PIXELS) {
it.remove();
continue;
}
boolean isCandidatePortrait = realWidth < realHeight;
int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
double distortion = Math.abs(aspectRatio - screenAspectRatio);
if (distortion > MAX_ASPECT_DISTORTION) {
it.remove();
continue;
}
if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
Point exactPoint = new Point(realWidth, realHeight);
Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
return exactPoint;
}
}
// If no exact match, use largest preview size. This was not a great idea on older devices because
// of the additional computation needed. We're likely to get here on newer Android 4+ devices, where
// the CPU is much more powerful.
if (!supportedPreviewSizes.isEmpty()) {
Camera.Size largestPreview = supportedPreviewSizes.get(0);
Point largestSize = new Point(largestPreview.width, largestPreview.height);
Log.i(TAG, "Using largest suitable preview size: " + largestSize);
return largestSize;
}
// If there is nothing at all suitable, return current preview size
Camera.Size defaultPreview = parameters.getPreviewSize();
if (defaultPreview == null) {
throw new IllegalStateException("Parameters contained no preview size!");
}
Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);
Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);
return defaultSize;
}
private static String findSettableValue(String name,
Collection<String> supportedValues,
String... desiredValues) {
Log.i(TAG, "Requesting " + name + " value from among: " + Arrays.toString(desiredValues));
Log.i(TAG, "Supported " + name + " values: " + supportedValues);
if (supportedValues != null) {
for (String desiredValue : desiredValues) {
if (supportedValues.contains(desiredValue)) {
Log.i(TAG, "Can set " + name + " to: " + desiredValue);
return desiredValue;
}
}
}
Log.i(TAG, "No supported values match");
return null;
}
private static String toString(Collection<int[]> arrays) {
if (arrays == null || arrays.isEmpty()) {
return "[]";
}
StringBuilder buffer = new StringBuilder();
buffer.append('[');
Iterator<int[]> it = arrays.iterator();
while (it.hasNext()) {
buffer.append(Arrays.toString(it.next()));
if (it.hasNext()) {
buffer.append(", ");
}
}
buffer.append(']');
return buffer.toString();
}
private static String toString(Iterable<Camera.Area> areas) {
if (areas == null) {
return null;
}
StringBuilder result = new StringBuilder();
for (Camera.Area area : areas) {
result.append(area.rect).append(':').append(area.weight).append(' ');
}
return result.toString();
}
public static String collectStats(Camera.Parameters parameters) {
return collectStats(parameters.flatten());
}
public static String collectStats(CharSequence flattenedParams) {
StringBuilder result = new StringBuilder(1000);
result.append("BOARD=").append(Build.BOARD).append('\n');
result.append("BRAND=").append(Build.BRAND).append('\n');
result.append("CPU_ABI=").append(Build.CPU_ABI).append('\n');
result.append("DEVICE=").append(Build.DEVICE).append('\n');
result.append("DISPLAY=").append(Build.DISPLAY).append('\n');
result.append("FINGERPRINT=").append(Build.FINGERPRINT).append('\n');
result.append("HOST=").append(Build.HOST).append('\n');
result.append("ID=").append(Build.ID).append('\n');
result.append("MANUFACTURER=").append(Build.MANUFACTURER).append('\n');
result.append("MODEL=").append(Build.MODEL).append('\n');
result.append("PRODUCT=").append(Build.PRODUCT).append('\n');
result.append("TAGS=").append(Build.TAGS).append('\n');
result.append("TIME=").append(Build.TIME).append('\n');
result.append("TYPE=").append(Build.TYPE).append('\n');
result.append("USER=").append(Build.USER).append('\n');
result.append("VERSION.CODENAME=").append(Build.VERSION.CODENAME).append('\n');
result.append("VERSION.INCREMENTAL=").append(Build.VERSION.INCREMENTAL).append('\n');
result.append("VERSION.RELEASE=").append(Build.VERSION.RELEASE).append('\n');
result.append("VERSION.SDK_INT=").append(Build.VERSION.SDK_INT).append('\n');
if (flattenedParams != null) {
String[] params = SEMICOLON.split(flattenedParams);
Arrays.sort(params);
for (String param : params) {
result.append(param).append('\n');
}
}
return result.toString();
}
}
/*
* 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.camera;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceHolder;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.client.android.camera.open.OpenCamera;
import com.google.zxing.client.android.camera.open.OpenCameraInterface;
import java.io.IOException;
/**
* This object wraps the Camera service object and expects to be the only one talking to it. The
* implementation encapsulates the steps needed to take preview-sized images, which are used for
* both preview and decoding.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class CameraManager {
private static final String TAG = CameraManager.class.getSimpleName();
private static final int MIN_FRAME_WIDTH = 240;
private static final int MIN_FRAME_HEIGHT = 240;
private static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920
private static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080
private final Context context;
private final CameraConfigurationManager configManager;
private OpenCamera camera;
private AutoFocusManager autoFocusManager;
private Rect framingRect;
private Rect framingRectInPreview;
private boolean initialized;
private boolean previewing;
private int requestedCameraId = OpenCameraInterface.NO_REQUESTED_CAMERA;
private int requestedFramingRectWidth;
private int requestedFramingRectHeight;
/**
* 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.
*/
private final PreviewCallback previewCallback;
public CameraManager(Context context) {
this.context = context;
this.configManager = new CameraConfigurationManager(context);
previewCallback = new PreviewCallback(configManager);
}
/**
* Opens the camera driver and initializes the hardware parameters.
*
* @param holder The surface object which the camera will draw preview frames into.
* @throws IOException Indicates the camera driver failed to open.
*/
public synchronized void openDriver(SurfaceHolder holder) throws IOException {
OpenCamera theCamera = camera;
if (theCamera == null) {
theCamera = OpenCameraInterface.open(requestedCameraId);
if (theCamera == null) {
throw new IOException("Camera.open() failed to return object from driver");
}
camera = theCamera;
}
if (!initialized) {
initialized = true;
configManager.initFromCameraParameters(theCamera);
if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0) {
setManualFramingRect(requestedFramingRectWidth, requestedFramingRectHeight);
requestedFramingRectWidth = 0;
requestedFramingRectHeight = 0;
}
}
Camera cameraObject = theCamera.getCamera();
Camera.Parameters parameters = cameraObject.getParameters();
String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save these, temporarily
try {
configManager.setDesiredCameraParameters(theCamera, false);
} catch (RuntimeException re) {
// Driver failed
Log.w(TAG, "Camera rejected parameters. Setting only minimal safe-mode parameters");
Log.i(TAG, "Resetting to saved camera params: " + parametersFlattened);
// Reset:
if (parametersFlattened != null) {
parameters = cameraObject.getParameters();
parameters.unflatten(parametersFlattened);
try {
cameraObject.setParameters(parameters);
configManager.setDesiredCameraParameters(theCamera, true);
} catch (RuntimeException re2) {
// Well, darn. Give up
Log.w(TAG, "Camera rejected even safe-mode parameters! No configuration");
}
}
}
cameraObject.setPreviewDisplay(holder);
}
public synchronized boolean isOpen() {
return camera != null;
}
/**
* Closes the camera driver if still in use.
*/
public synchronized void closeDriver() {
if (camera != null) {
camera.getCamera().release();
camera = null;
// Make sure to clear these each time we close the camera, so that any scanning rect
// requested by intent is forgotten.
framingRect = null;
framingRectInPreview = null;
}
}
/**
* Asks the camera hardware to begin drawing preview frames to the screen.
*/
public synchronized void startPreview() {
OpenCamera theCamera = camera;
if (theCamera != null && !previewing) {
theCamera.getCamera().startPreview();
previewing = true;
autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());
}
}
/**
* Tells the camera to stop drawing preview frames.
*/
public synchronized void stopPreview() {
if (autoFocusManager != null) {
autoFocusManager.stop();
autoFocusManager = null;
}
if (camera != null && previewing) {
camera.getCamera().stopPreview();
previewCallback.setHandler(null, 0);
previewing = false;
}
}
/**
* Convenience method for {@link com.google.zxing.client.android.CaptureActivity}
*
* @param newSetting if {@code true}, light should be turned on if currently off. And vice versa.
*/
public synchronized void setTorch(boolean newSetting) {
OpenCamera theCamera = camera;
if (theCamera != null) {
if (newSetting != configManager.getTorchState(theCamera.getCamera())) {
boolean wasAutoFocusManager = autoFocusManager != null;
if (wasAutoFocusManager) {
autoFocusManager.stop();
autoFocusManager = null;
}
configManager.setTorch(theCamera.getCamera(), newSetting);
if (wasAutoFocusManager) {
autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());
autoFocusManager.start();
}
}
}
}
/**
* A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
* in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
* respectively.
*
* @param handler The handler to send the message to.
* @param message The what field of the message to be sent.
*/
public synchronized void requestPreviewFrame(Handler handler, int message) {
OpenCamera theCamera = camera;
if (theCamera != null && previewing) {
previewCallback.setHandler(handler, message);
theCamera.getCamera().setOneShotPreviewCallback(previewCallback);
}
}
/**
* Calculates the framing rect which the UI should draw to show the user where to place the
* barcode. This target helps with alignment as well as forces the user to hold the device
* far enough away to ensure the image will be in focus.
*
* @return The rectangle to draw on screen in window coordinates.
*/
public synchronized Rect getFramingRect() {
if (framingRect == null) {
if (camera == null) {
return null;
}
Point screenResolution = configManager.getScreenResolution();
if (screenResolution == null) {
// Called early, before init even finished
return null;
}
int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
if(width<height) {
int leftOffset = (screenResolution.x - width) / 2;
int topOffset = (screenResolution.y - width) / 2;
framingRect = new Rect(leftOffset, topOffset, leftOffset + width,
topOffset + width);
} else {
int leftOffset = (screenResolution.x - height) / 2;
int topOffset = (screenResolution.y - height) / 2;
framingRect = new Rect(leftOffset, topOffset, leftOffset + height,
topOffset + height);
}
/*int leftOffset = (screenResolution.x - width) / 2;
int topOffset = (screenResolution.y - height) / 2;
framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);*/
Log.d(TAG, "Calculated framing rect: " + framingRect);
}
return framingRect;
}
private static int findDesiredDimensionInRange(int resolution, int hardMin, int hardMax) {
int dim = 5 * resolution / 8; // Target 5/8 of each dimension
if (dim < hardMin) {
return hardMin;
}
if (dim > hardMax) {
return hardMax;
}
return dim;
}
/**
* Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
* not UI / screen.
*
* @return {@link Rect} expressing barcode scan area in terms of the preview size
*/
public synchronized Rect getFramingRectInPreview() {
if (framingRectInPreview == null) {
Rect framingRect = getFramingRect();
if (framingRect == null) {
return null;
}
Rect rect = new Rect(framingRect);
Point cameraResolution = configManager.getCameraResolution();
Point screenResolution = configManager.getScreenResolution();
if (cameraResolution == null || screenResolution == null) {
// Called early, before init even finished
return null;
}
/* rect.left = rect.left * cameraResolution.x / screenResolution.x;
rect.right = rect.right * cameraResolution.x / screenResolution.x;
rect.top = rect.top * cameraResolution.y / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;*/
rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
framingRectInPreview = rect;
}
return framingRectInPreview;
}
/**
* Allows third party apps to specify the camera ID, rather than determine
* it automatically based on available cameras and their orientation.
*
* @param cameraId camera ID of the camera to use. A negative value means "no preference".
*/
public synchronized void setManualCameraId(int cameraId) {
requestedCameraId = cameraId;
}
/**
* Allows third party apps to specify the scanning rectangle dimensions, rather than determine
* them automatically based on screen resolution.
*
* @param width The width in pixels to scan.
* @param height The height in pixels to scan.
*/
public synchronized void setManualFramingRect(int width, int height) {
if (initialized) {
Point screenResolution = configManager.getScreenResolution();
if (width > screenResolution.x) {
width = screenResolution.x;
}
if (height > screenResolution.y) {
height = screenResolution.y;
}
int leftOffset = (screenResolution.x - width) / 2;
int topOffset = (screenResolution.y - height) / 2;
framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
Log.d(TAG, "Calculated manual framing rect: " + framingRect);
framingRectInPreview = null;
} else {
requestedFramingRectWidth = width;
requestedFramingRectHeight = height;
}
}
/**
* 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 PlanarYUVLuminanceSource instance.
*/
public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
Rect rect = getFramingRectInPreview();
if (rect == null) {
return null;
}
// Go ahead and assume it's YUV rather than die.
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
rect.width(), rect.height(), false);
}
}
/*
* Copyright (C) 2012 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.camera;
import android.content.SharedPreferences;
import com.google.zxing.client.android.PreferencesActivity;
/**
* Enumerates settings of the preference controlling the front light.
*/
public enum FrontLightMode {
/** Always on. */
ON,
/** On only when ambient light is low. */
AUTO,
/** Always off. */
OFF;
private static FrontLightMode parse(String modeString) {
return modeString == null ? OFF : valueOf(modeString);
}
public static FrontLightMode readPref(SharedPreferences sharedPrefs) {
return parse(sharedPrefs.getString(PreferencesActivity.KEY_FRONT_LIGHT_MODE, OFF.toString()));
}
}
/*
* Copyright (C) 2010 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.camera;
import android.graphics.Point;
import android.hardware.Camera;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
final class PreviewCallback implements Camera.PreviewCallback {
private static final String TAG = PreviewCallback.class.getSimpleName();
private final CameraConfigurationManager configManager;
private Handler previewHandler;
private int previewMessage;
PreviewCallback(CameraConfigurationManager configManager) {
this.configManager = configManager;
}
void setHandler(Handler previewHandler, int previewMessage) {
this.previewHandler = previewHandler;
this.previewMessage = previewMessage;
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Point cameraResolution = configManager.getCameraResolution();
Handler thePreviewHandler = previewHandler;
if (cameraResolution != null && thePreviewHandler != null) {
Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
cameraResolution.y, data);
message.sendToTarget();
previewHandler = null;
} else {
Log.d(TAG, "Got preview callback, but no handler or resolution available");
}
}
}
/*
* Copyright (C) 2015 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.camera.open;
public enum CameraFacing {
BACK, // must be value 0!
FRONT, // must be value 1!
}
/*
* Copyright (C) 2015 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.camera.open;
import android.hardware.Camera;
public final class OpenCamera {
private final int index;
private final Camera camera;
private final CameraFacing facing;
private final int orientation;
public OpenCamera(int index, Camera camera, CameraFacing facing, int orientation) {
this.index = index;
this.camera = camera;
this.facing = facing;
this.orientation = orientation;
}
public Camera getCamera() {
return camera;
}
public CameraFacing getFacing() {
return facing;
}
public int getOrientation() {
return orientation;
}
@Override
public String toString() {
return "Camera #" + index + " : " + facing + ',' + orientation;
}
}
/*
* Copyright (C) 2012 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.camera.open;
import android.hardware.Camera;
import android.util.Log;
public final class OpenCameraInterface {
private static final String TAG = OpenCameraInterface.class.getName();
private OpenCameraInterface() {
}
/** For {@link #open(int)}, means no preference for which camera to open. */
public static final int NO_REQUESTED_CAMERA = -1;
/**
* Opens the requested camera with {@link Camera#open(int)}, if one exists.
*
* @param cameraId camera ID of the camera to use. A negative value
* or {@link #NO_REQUESTED_CAMERA} means "no preference", in which case a rear-facing
* camera is returned if possible or else any camera
* @return handle to {@link OpenCamera} that was opened
*/
public static OpenCamera open(int cameraId) {
int numCameras = Camera.getNumberOfCameras();
if (numCameras == 0) {
Log.w(TAG, "No cameras!");
return null;
}
boolean explicitRequest = cameraId >= 0;
Camera.CameraInfo selectedCameraInfo = null;
int index;
if (explicitRequest) {
index = cameraId;
selectedCameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(index, selectedCameraInfo);
} else {
index = 0;
while (index < numCameras) {
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(index, cameraInfo);
CameraFacing reportedFacing = CameraFacing.values()[cameraInfo.facing];
if (reportedFacing == CameraFacing.BACK) {
selectedCameraInfo = cameraInfo;
break;
}
index++;
}
}
Camera camera;
if (index < numCameras) {
Log.i(TAG, "Opening camera #" + index);
camera = Camera.open(index);
} else {
if (explicitRequest) {
Log.w(TAG, "Requested camera does not exist: " + cameraId);
camera = null;
} else {
Log.i(TAG, "No camera facing " + CameraFacing.BACK + "; returning camera #0");
camera = Camera.open(0);
selectedCameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(0, selectedCameraInfo);
}
}
if (camera == null) {
return null;
}
return new OpenCamera(index,
camera,
CameraFacing.values()[selectedCameraInfo.facing],
selectedCameraInfo.orientation);
}
}
/*
* Copyright (C) 2011 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.encode;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
/**
* Implementations encode according to some scheme for encoding contact information, like VCard or
* MECARD.
*
* @author Sean Owen
*/
abstract class ContactEncoder {
/**
* @return first, the best effort encoding of all data in the appropriate format; second, a
* display-appropriate version of the contact information
*/
abstract String[] encode(List<String> names,
String organization,
List<String> addresses,
List<String> phones,
List<String> phoneTypes,
List<String> emails,
List<String> urls,
String note);
/**
* @return null if s is null or empty, or result of s.trim() otherwise
*/
static String trim(String s) {
if (s == null) {
return null;
}
String result = s.trim();
return result.isEmpty() ? null : result;
}
static void append(StringBuilder newContents,
StringBuilder newDisplayContents,
String prefix,
String value,
Formatter fieldFormatter,
char terminator) {
String trimmed = trim(value);
if (trimmed != null) {
newContents.append(prefix).append(fieldFormatter.format(trimmed, 0)).append(terminator);
newDisplayContents.append(trimmed).append('\n');
}
}
static void appendUpToUnique(StringBuilder newContents,
StringBuilder newDisplayContents,
String prefix,
List<String> values,
int max,
Formatter displayFormatter,
Formatter fieldFormatter,
char terminator) {
if (values == null) {
return;
}
int count = 0;
Collection<String> uniques = new HashSet<String>(2);
for (int i = 0; i < values.size(); i++) {
String value = values.get(i);
String trimmed = trim(value);
if (trimmed != null && !trimmed.isEmpty() && !uniques.contains(trimmed)) {
newContents.append(prefix).append(fieldFormatter.format(trimmed, i)).append(terminator);
CharSequence display = displayFormatter == null ? trimmed : displayFormatter.format(trimmed, i);
newDisplayContents.append(display).append('\n');
if (++count == max) {
break;
}
uniques.add(trimmed);
}
}
}
}
/*
* Copyright (C) 2011 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.encode;
/**
* Encapsulates some simple formatting logic, to aid refactoring in {@link ContactEncoder}.
*
* @author Sean Owen
*/
interface Formatter {
/**
* @param value value to format
* @param index index of value in a list of values to be formatted
* @return formatted value
*/
CharSequence format(CharSequence value, int index);
}
/*
* Copyright (C) 2011 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.encode;
import android.telephony.PhoneNumberUtils;
import java.util.List;
import java.util.regex.Pattern;
/**
* Encodes contact information according to the MECARD format.
*
* @author Sean Owen
*/
final class MECARDContactEncoder extends ContactEncoder {
private static final char TERMINATOR = ';';
@Override
public String[] encode(List<String> names,
String organization,
List<String> addresses,
List<String> phones,
List<String> phoneTypes,
List<String> emails,
List<String> urls,
String note) {
StringBuilder newContents = new StringBuilder(100);
newContents.append("MECARD:");
StringBuilder newDisplayContents = new StringBuilder(100);
Formatter fieldFormatter = new MECARDFieldFormatter();
appendUpToUnique(newContents, newDisplayContents, "N", names, 1, new
MECARDNameDisplayFormatter(), fieldFormatter, TERMINATOR);
append(newContents, newDisplayContents, "ORG", organization, fieldFormatter, TERMINATOR);
appendUpToUnique(newContents, newDisplayContents, "ADR", addresses, 1, null, fieldFormatter, TERMINATOR);
appendUpToUnique(newContents, newDisplayContents, "TEL", phones, Integer.MAX_VALUE,
new MECARDTelDisplayFormatter(), fieldFormatter, TERMINATOR);
appendUpToUnique(newContents, newDisplayContents, "EMAIL", emails, Integer.MAX_VALUE, null,
fieldFormatter, TERMINATOR);
appendUpToUnique(newContents, newDisplayContents, "URL", urls, Integer.MAX_VALUE, null,
fieldFormatter, TERMINATOR);
append(newContents, newDisplayContents, "NOTE", note, fieldFormatter, TERMINATOR);
newContents.append(';');
return new String[] { newContents.toString(), newDisplayContents.toString() };
}
private static class MECARDFieldFormatter implements Formatter {
private static final Pattern RESERVED_MECARD_CHARS = Pattern.compile("([\\\\:;])");
private static final Pattern NEWLINE = Pattern.compile("\\n");
@Override
public CharSequence format(CharSequence value, int index) {
return ':' + NEWLINE.matcher(RESERVED_MECARD_CHARS.matcher(value).replaceAll("\\\\$1")).replaceAll("");
}
}
private static class MECARDTelDisplayFormatter implements Formatter {
private static final Pattern NOT_DIGITS = Pattern.compile("[^0-9]+");
@Override
public CharSequence format(CharSequence value, int index) {
return NOT_DIGITS.matcher(PhoneNumberUtils.formatNumber(value.toString())).replaceAll("");
}
}
private static class MECARDNameDisplayFormatter implements Formatter {
private static final Pattern COMMA = Pattern.compile(",");
@Override
public CharSequence format(CharSequence value, int index) {
return COMMA.matcher(value).replaceAll("");
}
}
}
/*
* 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.encode;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.client.android.Contents;
import com.google.zxing.client.android.Intents;
import com.google.zxing.client.result.AddressBookParsedResult;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ResultParser;
import com.google.zxing.common.BitMatrix;
import com.senthink.ebike_administrer_tool.R;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
/**
* This class does the work of decoding the user's request and extracting all the data
* to be encoded in a barcode.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
final class QRCodeEncoder {
private static final String TAG = QRCodeEncoder.class.getSimpleName();
private static final int WHITE = 0xFFFFFFFF;
private static final int BLACK = 0xFF000000;
private final Context activity;
private String contents;
private String displayContents;
private String title;
private BarcodeFormat format;
private final int dimension;
private final boolean useVCard;
QRCodeEncoder(Context activity, Intent intent, int dimension, boolean useVCard) throws WriterException {
this.activity = activity;
this.dimension = dimension;
this.useVCard = useVCard;
String action = intent.getAction();
if (action.equals(Intents.Encode.ACTION)) {
encodeContentsFromZXingIntent(intent);
} else if (action.equals(Intent.ACTION_SEND)) {
encodeContentsFromShareIntent(intent);
}
}
String getContents() {
return contents;
}
String getDisplayContents() {
return displayContents;
}
String getTitle() {
return title;
}
boolean isUseVCard() {
return useVCard;
}
// It would be nice if the string encoding lived in the core ZXing library,
// but we use platform specific code like PhoneNumberUtils, so it can't.
private boolean encodeContentsFromZXingIntent(Intent intent) {
// Default to QR_CODE if no format given.
String formatString = intent.getStringExtra(Intents.Encode.FORMAT);
format = null;
if (formatString != null) {
try {
format = BarcodeFormat.valueOf(formatString);
} catch (IllegalArgumentException iae) {
// Ignore it then
}
}
if (format == null || format == BarcodeFormat.QR_CODE) {
String type = intent.getStringExtra(Intents.Encode.TYPE);
if (type == null || type.isEmpty()) {
return false;
}
this.format = BarcodeFormat.QR_CODE;
encodeQRCodeContents(intent, type);
} else {
String data = intent.getStringExtra(Intents.Encode.DATA);
if (data != null && !data.isEmpty()) {
contents = data;
displayContents = data;
title = activity.getString(R.string.contents_text);
}
}
return contents != null && !contents.isEmpty();
}
// Handles send intents from multitude of Android applications
private void encodeContentsFromShareIntent(Intent intent) throws WriterException {
// Check if this is a plain text encoding, or contact
if (intent.hasExtra(Intent.EXTRA_STREAM)) {
encodeFromStreamExtra(intent);
} else {
encodeFromTextExtras(intent);
}
}
private void encodeFromTextExtras(Intent intent) throws WriterException {
// Notice: Google Maps shares both URL and details in one text, bummer!
String theContents = ContactEncoder.trim(intent.getStringExtra(Intent.EXTRA_TEXT));
if (theContents == null) {
theContents = ContactEncoder.trim(intent.getStringExtra("android.intent.extra.HTML_TEXT"));
// Intent.EXTRA_HTML_TEXT
if (theContents == null) {
theContents = ContactEncoder.trim(intent.getStringExtra(Intent.EXTRA_SUBJECT));
if (theContents == null) {
String[] emails = intent.getStringArrayExtra(Intent.EXTRA_EMAIL);
if (emails != null) {
theContents = ContactEncoder.trim(emails[0]);
} else {
theContents = "?";
}
}
}
}
// Trim text to avoid URL breaking.
if (theContents == null || theContents.isEmpty()) {
throw new WriterException("Empty EXTRA_TEXT");
}
contents = theContents;
// We only do QR code.
format = BarcodeFormat.QR_CODE;
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) {
displayContents = intent.getStringExtra(Intent.EXTRA_SUBJECT);
} else if (intent.hasExtra(Intent.EXTRA_TITLE)) {
displayContents = intent.getStringExtra(Intent.EXTRA_TITLE);
} else {
displayContents = contents;
}
title = activity.getString(R.string.contents_text);
}
// Handles send intents from the Contacts app, retrieving a contact as a VCARD.
private void encodeFromStreamExtra(Intent intent) throws WriterException {
format = BarcodeFormat.QR_CODE;
Bundle bundle = intent.getExtras();
if (bundle == null) {
throw new WriterException("No extras");
}
Uri uri = bundle.getParcelable(Intent.EXTRA_STREAM);
if (uri == null) {
throw new WriterException("No EXTRA_STREAM");
}
byte[] vcard;
String vcardString;
InputStream stream = null;
try {
stream = activity.getContentResolver().openInputStream(uri);
if (stream == null) {
throw new WriterException("Can't open stream for " + uri);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = stream.read(buffer)) > 0) {
baos.write(buffer, 0, bytesRead);
}
vcard = baos.toByteArray();
vcardString = new String(vcard, 0, vcard.length, "UTF-8");
} catch (IOException ioe) {
throw new WriterException(ioe);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// continue
}
}
}
Log.d(TAG, "Encoding share intent content:");
Log.d(TAG, vcardString);
Result result = new Result(vcardString, vcard, null, BarcodeFormat.QR_CODE);
ParsedResult parsedResult = ResultParser.parseResult(result);
if (!(parsedResult instanceof AddressBookParsedResult)) {
throw new WriterException("Result was not an address");
}
encodeQRCodeContents((AddressBookParsedResult) parsedResult);
if (contents == null || contents.isEmpty()) {
throw new WriterException("No content to encode");
}
}
private void encodeQRCodeContents(Intent intent, String type) {
if (type == Contents.Type.TEXT) {
String textData = intent.getStringExtra(Intents.Encode.DATA);
if (textData != null && !textData.isEmpty()) {
contents = textData;
displayContents = textData;
title = activity.getString(R.string.contents_text);
}
} else if (type == Contents.Type.EMAIL) {
String emailData = ContactEncoder.trim(intent
.getStringExtra(Intents.Encode.DATA));
if (emailData != null) {
contents = "mailto:" + emailData;
displayContents = emailData;
title = activity.getString(R.string.contents_email);
}
} else if (type == Contents.Type.PHONE) {
String phoneData = ContactEncoder.trim(intent
.getStringExtra(Intents.Encode.DATA));
if (phoneData != null) {
contents = "tel:" + phoneData;
displayContents = PhoneNumberUtils.formatNumber(phoneData);
title = activity.getString(R.string.contents_phone);
}
} else if (type == Contents.Type.SMS) {
String smsData = ContactEncoder.trim(intent
.getStringExtra(Intents.Encode.DATA));
if (smsData != null) {
contents = "sms:" + smsData;
displayContents = PhoneNumberUtils.formatNumber(smsData);
title = activity.getString(R.string.contents_sms);
}
} else if (type == Contents.Type.CONTACT) {
Bundle contactBundle = intent.getBundleExtra(Intents.Encode.DATA);
if (contactBundle != null) {
String name = contactBundle
.getString(ContactsContract.Intents.Insert.NAME);
String organization = contactBundle
.getString(ContactsContract.Intents.Insert.COMPANY);
String address = contactBundle
.getString(ContactsContract.Intents.Insert.POSTAL);
List<String> phones = getAllBundleValues(contactBundle,
Contents.PHONE_KEYS);
List<String> phoneTypes = getAllBundleValues(contactBundle,
Contents.PHONE_TYPE_KEYS);
List<String> emails = getAllBundleValues(contactBundle,
Contents.EMAIL_KEYS);
String url = contactBundle.getString(Contents.URL_KEY);
List<String> urls = url == null ? null : Collections
.singletonList(url);
String note = contactBundle.getString(Contents.NOTE_KEY);
ContactEncoder encoder = useVCard ? new VCardContactEncoder()
: new MECARDContactEncoder();
String[] encoded = encoder.encode(
Collections.singletonList(name), organization,
Collections.singletonList(address), phones, phoneTypes,
emails, urls, note);
// Make sure we've encoded at least one field.
if (!encoded[1].isEmpty()) {
contents = encoded[0];
displayContents = encoded[1];
title = activity.getString(R.string.contents_contact);
}
}
} else if (type == Contents.Type.LOCATION) {
Bundle locationBundle = intent.getBundleExtra(Intents.Encode.DATA);
if (locationBundle != null) {
// These must use Bundle.getFloat(), not getDouble(), it's part
// of the API.
float latitude = locationBundle
.getFloat("LAT", Float.MAX_VALUE);
float longitude = locationBundle.getFloat("LONG",
Float.MAX_VALUE);
if (latitude != Float.MAX_VALUE && longitude != Float.MAX_VALUE) {
contents = "geo:" + latitude + ',' + longitude;
displayContents = latitude + "," + longitude;
title = activity.getString(R.string.contents_location);
}
}
}
/* switch (type) {
case Contents.Type.TEXT:
String textData = intent.getStringExtra(Intents.Encode.DATA);
if (textData != null && !textData.isEmpty()) {
contents = textData;
displayContents = textData;
title = activity.getString(R.string.contents_text);
}
break;
case Contents.Type.EMAIL:
String emailData = ContactEncoder.trim(intent.getStringExtra(Intents.Encode.DATA));
if (emailData != null) {
contents = "mailto:" + emailData;
displayContents = emailData;
title = activity.getString(R.string.contents_email);
}
break;
case Contents.Type.PHONE:
String phoneData = ContactEncoder.trim(intent.getStringExtra(Intents.Encode.DATA));
if (phoneData != null) {
contents = "tel:" + phoneData;
displayContents = PhoneNumberUtils.formatNumber(phoneData);
title = activity.getString(R.string.contents_phone);
}
break;
case Contents.Type.SMS:
String smsData = ContactEncoder.trim(intent.getStringExtra(Intents.Encode.DATA));
if (smsData != null) {
contents = "sms:" + smsData;
displayContents = PhoneNumberUtils.formatNumber(smsData);
title = activity.getString(R.string.contents_sms);
}
break;
case Contents.Type.CONTACT:
Bundle contactBundle = intent.getBundleExtra(Intents.Encode.DATA);
if (contactBundle != null) {
String name = contactBundle.getString(ContactsContract.Intents.Insert.NAME);
String organization = contactBundle.getString(ContactsContract.Intents.Insert.COMPANY);
String address = contactBundle.getString(ContactsContract.Intents.Insert.POSTAL);
List<String> phones = getAllBundleValues(contactBundle, Contents.PHONE_KEYS);
List<String> phoneTypes = getAllBundleValues(contactBundle, Contents.PHONE_TYPE_KEYS);
List<String> emails = getAllBundleValues(contactBundle, Contents.EMAIL_KEYS);
String url = contactBundle.getString(Contents.URL_KEY);
List<String> urls = url == null ? null : Collections.singletonList(url);
String note = contactBundle.getString(Contents.NOTE_KEY);
ContactEncoder encoder = useVCard ? new VCardContactEncoder() : new MECARDContactEncoder();
String[] encoded = encoder.encode(Collections.singletonList(name),
organization,
Collections.singletonList(address),
phones,
phoneTypes,
emails,
urls,
note);
// Make sure we've encoded at least one field.
if (!encoded[1].isEmpty()) {
contents = encoded[0];
displayContents = encoded[1];
title = activity.getString(R.string.contents_contact);
}
}
break;
case Contents.Type.LOCATION:
Bundle locationBundle = intent.getBundleExtra(Intents.Encode.DATA);
if (locationBundle != null) {
// These must use Bundle.getFloat(), not getDouble(), it's part of the API.
float latitude = locationBundle.getFloat("LAT", Float.MAX_VALUE);
float longitude = locationBundle.getFloat("LONG", Float.MAX_VALUE);
if (latitude != Float.MAX_VALUE && longitude != Float.MAX_VALUE) {
contents = "geo:" + latitude + ',' + longitude;
displayContents = latitude + "," + longitude;
title = activity.getString(R.string.contents_location);
}
}
break;
}*/
}
private static List<String> getAllBundleValues(Bundle bundle, String[] keys) {
List<String> values = new ArrayList<String>(keys.length);
for (String key : keys) {
Object value = bundle.get(key);
values.add(value == null ? null : value.toString());
}
return values;
}
private void encodeQRCodeContents(AddressBookParsedResult contact) {
ContactEncoder encoder = useVCard ? new VCardContactEncoder() : new MECARDContactEncoder();
String[] encoded = encoder.encode(toList(contact.getNames()),
contact.getOrg(),
toList(contact.getAddresses()),
toList(contact.getPhoneNumbers()),
null,
toList(contact.getEmails()),
toList(contact.getURLs()),
null);
// Make sure we've encoded at least one field.
if (!encoded[1].isEmpty()) {
contents = encoded[0];
displayContents = encoded[1];
title = activity.getString(R.string.contents_contact);
}
}
private static List<String> toList(String[] values) {
return values == null ? null : Arrays.asList(values);
}
Bitmap encodeAsBitmap() throws WriterException {
String contentsToEncode = contents;
if (contentsToEncode == null) {
return null;
}
Map<EncodeHintType,Object> hints = null;
String encoding = guessAppropriateEncoding(contentsToEncode);
if (encoding != null) {
hints = new EnumMap<EncodeHintType, Object>(EncodeHintType.class);
hints.put(EncodeHintType.CHARACTER_SET, encoding);
}
BitMatrix result;
try {
result = new MultiFormatWriter().encode(contentsToEncode, format, dimension, dimension, hints);
} catch (IllegalArgumentException iae) {
// Unsupported format
return null;
}
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
private static String guessAppropriateEncoding(CharSequence contents) {
// Very crude at the moment
for (int i = 0; i < contents.length(); i++) {
if (contents.charAt(i) > 0xFF) {
return "UTF-8";
}
}
return null;
}
}
/*
* Copyright (C) 2011 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.encode;
import android.provider.ContactsContract;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Encodes contact information according to the vCard format.
*
* @author Sean Owen
*/
final class VCardContactEncoder extends ContactEncoder {
private static final char TERMINATOR = '\n';
@Override
public String[] encode(List<String> names,
String organization,
List<String> addresses,
List<String> phones,
List<String> phoneTypes,
List<String> emails,
List<String> urls,
String note) {
StringBuilder newContents = new StringBuilder(100);
newContents.append("BEGIN:VCARD").append(TERMINATOR);
newContents.append("VERSION:3.0").append(TERMINATOR);
StringBuilder newDisplayContents = new StringBuilder(100);
Formatter fieldFormatter = new VCardFieldFormatter();
appendUpToUnique(newContents, newDisplayContents, "N", names, 1, null, fieldFormatter, TERMINATOR);
append(newContents, newDisplayContents, "ORG", organization, fieldFormatter, TERMINATOR);
appendUpToUnique(newContents, newDisplayContents, "ADR", addresses, 1, null, fieldFormatter, TERMINATOR);
List<Map<String,Set<String>>> phoneMetadata = buildPhoneMetadata(phones, phoneTypes);
appendUpToUnique(newContents, newDisplayContents, "TEL", phones, Integer.MAX_VALUE,
new VCardTelDisplayFormatter(phoneMetadata),
new VCardFieldFormatter(phoneMetadata), TERMINATOR);
appendUpToUnique(newContents, newDisplayContents, "EMAIL", emails, Integer.MAX_VALUE, null,
fieldFormatter, TERMINATOR);
appendUpToUnique(newContents, newDisplayContents, "URL", urls, Integer.MAX_VALUE, null,
fieldFormatter, TERMINATOR);
append(newContents, newDisplayContents, "NOTE", note, fieldFormatter, TERMINATOR);
newContents.append("END:VCARD").append(TERMINATOR);
return new String[] { newContents.toString(), newDisplayContents.toString() };
}
static List<Map<String,Set<String>>> buildPhoneMetadata(Collection<String> phones, List<String> phoneTypes) {
if (phoneTypes == null || phoneTypes.isEmpty()) {
return null;
}
List<Map<String,Set<String>>> metadataForIndex = new ArrayList<Map<String, Set<String>>>();
for (int i = 0; i < phones.size(); i++) {
if (phoneTypes.size() <= i) {
metadataForIndex.add(null);
} else {
Map<String,Set<String>> metadata = new HashMap<String, Set<String>>();
metadataForIndex.add(metadata);
Set<String> typeTokens = new HashSet<String>();
metadata.put("TYPE", typeTokens);
String typeString = phoneTypes.get(i);
Integer androidType = maybeIntValue(typeString);
if (androidType == null) {
typeTokens.add(typeString);
} else {
String purpose = vCardPurposeLabelForAndroidType(androidType);
String context = vCardContextLabelForAndroidType(androidType);
if (purpose != null) {
typeTokens.add(purpose);
}
if (context != null) {
typeTokens.add(context);
}
}
}
}
return metadataForIndex;
}
private static Integer maybeIntValue(String value) {
try {
return Integer.valueOf(value);
} catch (NumberFormatException nfe) {
return null;
}
}
private static String vCardPurposeLabelForAndroidType(int androidType) {
switch (androidType) {
case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME:
case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK:
case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX:
return "fax";
case ContactsContract.CommonDataKinds.Phone.TYPE_PAGER:
case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER:
return "pager";
case ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD:
return "textphone";
case ContactsContract.CommonDataKinds.Phone.TYPE_MMS:
return "text";
default:
return null;
}
}
private static String vCardContextLabelForAndroidType(int androidType) {
switch (androidType) {
case ContactsContract.CommonDataKinds.Phone.TYPE_HOME:
case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE:
case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME:
case ContactsContract.CommonDataKinds.Phone.TYPE_PAGER:
return "home";
case ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN:
case ContactsContract.CommonDataKinds.Phone.TYPE_WORK:
case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE:
case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK:
case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER:
return "work";
default:
return null;
}
}
}
/*
* Copyright (C) 2014 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.encode;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
/**
* @author Sean Owen
*/
final class VCardFieldFormatter implements Formatter {
private static final Pattern RESERVED_VCARD_CHARS = Pattern.compile("([\\\\,;])");
private static final Pattern NEWLINE = Pattern.compile("\\n");
private final List<Map<String,Set<String>>> metadataForIndex;
VCardFieldFormatter() {
this(null);
}
VCardFieldFormatter(List<Map<String,Set<String>>> metadataForIndex) {
this.metadataForIndex = metadataForIndex;
}
@Override
public CharSequence format(CharSequence value, int index) {
value = RESERVED_VCARD_CHARS.matcher(value).replaceAll("\\\\$1");
value = NEWLINE.matcher(value).replaceAll("");
Map<String,Set<String>> metadata =
metadataForIndex == null || metadataForIndex.size() <= index ? null : metadataForIndex.get(index);
value = formatMetadata(value, metadata);
return value;
}
private static CharSequence formatMetadata(CharSequence value, Map<String,Set<String>> metadata) {
StringBuilder withMetadata = new StringBuilder();
if (metadata != null) {
for (Map.Entry<String,Set<String>> metadatum : metadata.entrySet()) {
Set<String> values = metadatum.getValue();
if (values == null || values.isEmpty()) {
continue;
}
withMetadata.append(';').append(metadatum.getKey()).append('=');
if (values.size() > 1) {
withMetadata.append('"');
}
Iterator<String> valuesIt = values.iterator();
withMetadata.append(valuesIt.next());
while (valuesIt.hasNext()) {
withMetadata.append(',').append(valuesIt.next());
}
if (values.size() > 1) {
withMetadata.append('"');
}
}
}
withMetadata.append(':').append(value);
return withMetadata;
}
}
/*
* Copyright (C) 2014 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.encode;
import android.telephony.PhoneNumberUtils;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Sean Owen
*/
final class VCardTelDisplayFormatter implements Formatter {
private final List<Map<String,Set<String>>> metadataForIndex;
VCardTelDisplayFormatter() {
this(null);
}
VCardTelDisplayFormatter(List<Map<String,Set<String>>> metadataForIndex) {
this.metadataForIndex = metadataForIndex;
}
@Override
public CharSequence format(CharSequence value, int index) {
value = PhoneNumberUtils.formatNumber(value.toString());
Map<String,Set<String>> metadata =
metadataForIndex == null || metadataForIndex.size() <= index ? null : metadataForIndex.get(index);
value = formatMetadata(value, metadata);
return value;
}
private static CharSequence formatMetadata(CharSequence value, Map<String,Set<String>> metadata) {
if (metadata == null || metadata.isEmpty()) {
return value;
}
StringBuilder withMetadata = new StringBuilder();
for (Map.Entry<String,Set<String>> metadatum : metadata.entrySet()) {
Set<String> values = metadatum.getValue();
if (values == null || values.isEmpty()) {
continue;
}
Iterator<String> valuesIt = values.iterator();
withMetadata.append(valuesIt.next());
while (valuesIt.hasNext()) {
withMetadata.append(',').append(valuesIt.next());
}
}
if (withMetadata.length() > 0) {
withMetadata.append(' ');
}
withMetadata.append(value);
return withMetadata;
}
}
/*
* 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.app.Activity;
import android.telephony.PhoneNumberUtils;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import com.google.zxing.client.result.AddressBookParsedResult;
import com.google.zxing.client.result.ParsedResult;
import com.senthink.ebike_administrer_tool.R;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Handles address book entries.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class AddressBookResultHandler extends ResultHandler {
private static final DateFormat[] DATE_FORMATS = {
new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH),
new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ENGLISH),
new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH),
};
static {
for (DateFormat format : DATE_FORMATS) {
format.setLenient(false);
}
}
private static final int[] BUTTON_TEXTS = {
R.string.button_add_contact,
R.string.button_show_map,
R.string.button_dial,
R.string.button_email,
};
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 < buttonCount) {
int count = -1;
for (int x = 0; x < MAX_BUTTON_COUNT; x++) {
if (fields[x]) {
count++;
}
if (count == index) {
return x;
}
}
}
return -1;
}
public AddressBookResultHandler(Activity activity, ParsedResult result) {
super(activity, result);
AddressBookParsedResult addressResult = (AddressBookParsedResult) result;
String[] addresses = addressResult.getAddresses();
boolean hasAddress = addresses != null && addresses.length > 0 && addresses[0] != null && !addresses[0].isEmpty();
String[] phoneNumbers = addressResult.getPhoneNumbers();
boolean hasPhoneNumber = phoneNumbers != null && phoneNumbers.length > 0;
String[] emails = addressResult.getEmails();
boolean hasEmailAddress = emails != null && emails.length > 0;
fields = new boolean[MAX_BUTTON_COUNT];
fields[0] = true; // Add contact is always available
fields[1] = hasAddress;
fields[2] = hasPhoneNumber;
fields[3] = hasEmailAddress;
buttonCount = 0;
for (int x = 0; x < MAX_BUTTON_COUNT; x++) {
if (fields[x]) {
buttonCount++;
}
}
}
@Override
public int getButtonCount() {
return buttonCount;
}
@Override
public int getButtonText(int index) {
return BUTTON_TEXTS[mapIndexToAction(index)];
}
@Override
public void handleButtonPress(int index) {
AddressBookParsedResult addressResult = (AddressBookParsedResult) getResult();
String[] addresses = addressResult.getAddresses();
String address1 = addresses == null || addresses.length < 1 ? null : addresses[0];
String[] addressTypes = addressResult.getAddressTypes();
String address1Type = addressTypes == null || addressTypes.length < 1 ? null : addressTypes[0];
int action = mapIndexToAction(index);
switch (action) {
case 0:
addContact(addressResult.getNames(),
addressResult.getNicknames(),
addressResult.getPronunciation(),
addressResult.getPhoneNumbers(),
addressResult.getPhoneTypes(),
addressResult.getEmails(),
addressResult.getEmailTypes(),
addressResult.getNote(),
addressResult.getInstantMessenger(),
address1,
address1Type,
addressResult.getOrg(),
addressResult.getTitle(),
addressResult.getURLs(),
addressResult.getBirthday(),
addressResult.getGeo());
break;
case 1:
searchMap(address1);
break;
case 2:
dialPhone(addressResult.getPhoneNumbers()[0]);
break;
case 3:
sendEmail(addressResult.getEmails(), null, null, null, null);
break;
default:
break;
}
}
private static Date parseDate(String s) {
for (DateFormat currentFormat : DATE_FORMATS) {
try {
return currentFormat.parse(s);
} catch (ParseException e) {
// continue
}
}
return null;
}
// Overriden so we can hyphenate phone numbers, format birthdays, and bold the name.
@Override
public CharSequence getDisplayContents() {
AddressBookParsedResult result = (AddressBookParsedResult) getResult();
StringBuilder contents = new StringBuilder(100);
ParsedResult.maybeAppend(result.getNames(), contents);
int namesLength = contents.length();
String pronunciation = result.getPronunciation();
if (pronunciation != null && !pronunciation.isEmpty()) {
contents.append("\n(");
contents.append(pronunciation);
contents.append(')');
}
ParsedResult.maybeAppend(result.getTitle(), contents);
ParsedResult.maybeAppend(result.getOrg(), contents);
ParsedResult.maybeAppend(result.getAddresses(), contents);
String[] numbers = result.getPhoneNumbers();
if (numbers != null) {
for (String number : numbers) {
if (number != null) {
ParsedResult.maybeAppend(PhoneNumberUtils.formatNumber(number), contents);
}
}
}
ParsedResult.maybeAppend(result.getEmails(), contents);
ParsedResult.maybeAppend(result.getURLs(), contents);
String birthday = result.getBirthday();
if (birthday != null && !birthday.isEmpty()) {
Date date = parseDate(birthday);
if (date != null) {
ParsedResult.maybeAppend(DateFormat.getDateInstance(DateFormat.MEDIUM).format(date.getTime()), contents);
}
}
ParsedResult.maybeAppend(result.getNote(), contents);
if (namesLength > 0) {
// Bold the full name to make it stand out a bit.
Spannable styled = new SpannableString(contents.toString());
styled.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, namesLength, 0);
return styled;
} else {
return contents.toString();
}
}
@Override
public int getDisplayTitle() {
return R.string.result_address_book;
}
}
/*
* 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.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.util.Log;
import com.google.zxing.client.result.CalendarParsedResult;
import com.google.zxing.client.result.ParsedResult;
import com.senthink.ebike_administrer_tool.R;
import java.text.DateFormat;
import java.util.Date;
/**
* Handles calendar entries encoded in QR Codes.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen
*/
public final class CalendarResultHandler extends ResultHandler {
private static final String TAG = CalendarResultHandler.class.getSimpleName();
private static final int[] buttons = {
R.string.button_add_calendar
};
public CalendarResultHandler(Activity activity, ParsedResult result) {
super(activity, result);
}
@Override
public int getButtonCount() {
return buttons.length;
}
@Override
public int getButtonText(int index) {
return buttons[index];
}
@Override
public void handleButtonPress(int index) {
if (index == 0) {
CalendarParsedResult calendarResult = (CalendarParsedResult) getResult();
String description = calendarResult.getDescription();
String organizer = calendarResult.getOrganizer();
if (organizer != null) { // No separate Intent key, put in description
if (description == null) {
description = organizer;
} else {
description = description + '\n' + organizer;
}
}
addCalendarEvent(calendarResult.getSummary(),
calendarResult.getStart(),
calendarResult.isStartAllDay(),
calendarResult.getEnd(),
calendarResult.getLocation(),
description,
calendarResult.getAttendees());
}
}
/**
* Sends an intent to create a new calendar event by prepopulating the Add Event UI. Older
* versions of the system have a bug where the event title will not be filled out.
*
* @param summary A description of the event
* @param start The start time
* @param allDay if true, event is considered to be all day starting from start time
* @param end The end time (optional)
* @param location a text description of the event location
* @param description a text description of the event itself
* @param attendees attendees to invite
*/
private void addCalendarEvent(String summary,
Date start,
boolean allDay,
Date end,
String location,
String description,
String[] attendees) {
Intent intent = new Intent(Intent.ACTION_INSERT);
intent.setType("vnd.android.cursor.item/event");
long startMilliseconds = start.getTime();
intent.putExtra("beginTime", startMilliseconds);
if (allDay) {
intent.putExtra("allDay", true);
}
long endMilliseconds;
if (end == null) {
if (allDay) {
// + 1 day
endMilliseconds = startMilliseconds + 24 * 60 * 60 * 1000;
} else {
endMilliseconds = startMilliseconds;
}
} else {
endMilliseconds = end.getTime();
}
intent.putExtra("endTime", endMilliseconds);
intent.putExtra("title", summary);
intent.putExtra("eventLocation", location);
intent.putExtra("description", description);
if (attendees != null) {
intent.putExtra(Intent.EXTRA_EMAIL, attendees);
// Documentation says this is either a String[] or comma-separated String, which is right?
}
try {
// Do this manually at first
rawLaunchIntent(intent);
} catch (ActivityNotFoundException anfe) {
Log.w(TAG, "No calendar app available that responds to " + Intent.ACTION_INSERT);
// For calendar apps that don't like "INSERT":
intent.setAction(Intent.ACTION_EDIT);
launchIntent(intent); // Fail here for real if nothing can handle it
}
}
@Override
public CharSequence getDisplayContents() {
CalendarParsedResult calResult = (CalendarParsedResult) getResult();
StringBuilder result = new StringBuilder(100);
ParsedResult.maybeAppend(calResult.getSummary(), result);
Date start = calResult.getStart();
ParsedResult.maybeAppend(format(calResult.isStartAllDay(), start), result);
Date end = calResult.getEnd();
if (end != null) {
if (calResult.isEndAllDay() && !start.equals(end)) {
// Show only year/month/day
// if it's all-day and this is the end date, it's exclusive, so show the user
// that it ends on the day before to make more intuitive sense.
// But don't do it if the event already (incorrectly?) specifies the same start/end
end = new Date(end.getTime() - 24 * 60 * 60 * 1000);
}
ParsedResult.maybeAppend(format(calResult.isEndAllDay(), end), result);
}
ParsedResult.maybeAppend(calResult.getLocation(), result);
ParsedResult.maybeAppend(calResult.getOrganizer(), result);
ParsedResult.maybeAppend(calResult.getAttendees(), result);
ParsedResult.maybeAppend(calResult.getDescription(), result);
return result.toString();
}
private static String format(boolean allDay, Date date) {
if (date == null) {
return null;
}
DateFormat format = allDay
? DateFormat.getDateInstance(DateFormat.MEDIUM)
: DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
return format.format(date);
}
@Override
public int getDisplayTitle() {
return R.string.result_calendar;
}
}
/*
* 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.app.Activity;
import com.google.zxing.client.result.EmailAddressParsedResult;
import com.google.zxing.client.result.ParsedResult;
import com.senthink.ebike_administrer_tool.R;
/**
* 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
};
public EmailAddressResultHandler(Activity activity, ParsedResult result) {
super(activity, result);
}
@Override
public int getButtonCount() {
return buttons.length;
}
@Override
public int getButtonText(int index) {
return buttons[index];
}
@Override
public void handleButtonPress(int index) {
EmailAddressParsedResult emailResult = (EmailAddressParsedResult) getResult();
switch (index) {
case 0:
sendEmail(emailResult.getTos(),
emailResult.getCCs(),
emailResult.getBCCs(),
emailResult.getSubject(),
emailResult.getBody());
break;
case 1:
addEmailOnlyContact(emailResult.getTos(), null);
break;
}
}
@Override
public int getDisplayTitle() {
return R.string.result_email_address;
}
}
/*
* 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.app.Activity;
import com.google.zxing.client.result.GeoParsedResult;
import com.google.zxing.client.result.ParsedResult;
import com.senthink.ebike_administrer_tool.R;
/**
* 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
};
public GeoResultHandler(Activity activity, ParsedResult result) {
super(activity, result);
}
@Override
public int getButtonCount() {
return buttons.length;
}
@Override
public int getButtonText(int index) {
return buttons[index];
}
@Override
public void handleButtonPress(int index) {
GeoParsedResult geoResult = (GeoParsedResult) getResult();
switch (index) {
case 0:
openMap(geoResult.getGeoURI());
break;
case 1:
getDirections(geoResult.getLatitude(), geoResult.getLongitude());
break;
}
}
@Override
public int getDisplayTitle() {
return R.string.result_geo;
}
}
/*
* 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.app.Activity;
import com.google.zxing.Result;
import com.google.zxing.client.result.ISBNParsedResult;
import com.google.zxing.client.result.ParsedResult;
import com.senthink.ebike_administrer_tool.R;
/**
* 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
};
public ISBNResultHandler(Activity activity, ParsedResult result, Result rawResult) {
super(activity, result, rawResult);
}
@Override
public int getButtonCount() {
return hasCustomProductSearch() ? buttons.length : buttons.length - 1;
}
@Override
public int getButtonText(int index) {
return buttons[index];
}
@Override
public void handleButtonPress(int index) {
ISBNParsedResult isbnResult = (ISBNParsedResult) getResult();
switch (index) {
case 0:
openProductSearch(isbnResult.getISBN());
break;
case 1:
openBookSearch(isbnResult.getISBN());
break;
case 2:
searchBookContents(isbnResult.getISBN());
break;
case 3:
openURL(fillInCustomSearchURL(isbnResult.getISBN()));
break;
}
}
@Override
public int getDisplayTitle() {
return R.string.result_isbn;
}
}
/*
* 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.app.Activity;
import com.google.zxing.Result;
import com.google.zxing.client.result.ExpandedProductParsedResult;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ProductParsedResult;
import com.senthink.ebike_administrer_tool.R;
/**
* 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
};
public ProductResultHandler(Activity activity, ParsedResult result, Result rawResult) {
super(activity, result, rawResult);
}
@Override
public int getButtonCount() {
return hasCustomProductSearch() ? buttons.length : buttons.length - 1;
}
@Override
public int getButtonText(int index) {
return buttons[index];
}
@Override
public void handleButtonPress(int index) {
String productID = getProductIDFromResult(getResult());
switch (index) {
case 0:
openProductSearch(productID);
break;
case 1:
webSearch(productID);
break;
case 2:
openURL(fillInCustomSearchURL(productID));
break;
}
}
private static String getProductIDFromResult(ParsedResult rawResult) {
if (rawResult instanceof ProductParsedResult) {
return ((ProductParsedResult) rawResult).getNormalizedProductID();
}
if (rawResult instanceof ExpandedProductParsedResult) {
return ((ExpandedProductParsedResult) rawResult).getRawText();
}
throw new IllegalArgumentException(rawResult.getClass().toString());
}
@Override
public int getDisplayTitle() {
return R.string.result_product;
}
}
/*
* 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.view.View;
/**
* 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 View.OnClickListener {
private final ResultHandler resultHandler;
private final int index;
public ResultButtonListener(ResultHandler resultHandler, int index) {
this.resultHandler = resultHandler;
this.index = index;
}
@Override
public void onClick(View view) {
resultHandler.handleButtonPress(index);
}
}
/*
* 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.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ContentValues;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.util.Log;
import com.google.zxing.Result;
import com.google.zxing.client.android.Contents;
import com.google.zxing.client.android.LocaleManager;
import com.google.zxing.client.android.PreferencesActivity;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ParsedResultType;
import com.google.zxing.client.result.ResultParser;
import com.senthink.ebike_administrer_tool.R;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Locale;
/**
* 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)
* @author Sean Owen
*/
public abstract class ResultHandler {
private static final String TAG = ResultHandler.class.getSimpleName();
private static final String[] EMAIL_TYPE_STRINGS = {"home", "work", "mobile"};
private static final String[] PHONE_TYPE_STRINGS = {"home", "work", "mobile", "fax", "pager", "main"};
private static final String[] ADDRESS_TYPE_STRINGS = {"home", "work"};
private static final int[] EMAIL_TYPE_VALUES = {
ContactsContract.CommonDataKinds.Email.TYPE_HOME,
ContactsContract.CommonDataKinds.Email.TYPE_WORK,
ContactsContract.CommonDataKinds.Email.TYPE_MOBILE,
};
private static final int[] PHONE_TYPE_VALUES = {
ContactsContract.CommonDataKinds.Phone.TYPE_HOME,
ContactsContract.CommonDataKinds.Phone.TYPE_WORK,
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK,
ContactsContract.CommonDataKinds.Phone.TYPE_PAGER,
ContactsContract.CommonDataKinds.Phone.TYPE_MAIN,
};
private static final int[] ADDRESS_TYPE_VALUES = {
ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME,
ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK,
};
private static final int NO_TYPE = -1;
public static final int MAX_BUTTON_COUNT = 4;
private final ParsedResult result;
private final Activity activity;
private final Result rawResult;
private final String customProductSearch;
ResultHandler(Activity activity, ParsedResult result) {
this(activity, result, null);
}
ResultHandler(Activity activity, ParsedResult result, Result rawResult) {
this.result = result;
this.activity = activity;
this.rawResult = rawResult;
this.customProductSearch = parseCustomSearchURL();
}
public final ParsedResult getResult() {
return result;
}
final boolean hasCustomProductSearch() {
return customProductSearch != null;
}
final Activity getActivity() {
return activity;
}
/**
* Indicates how many buttons the derived class wants shown.
*
* @return The integer button count.
*/
public abstract int getButtonCount();
/**
* The text of the nth action button.
*
* @param index From 0 to getButtonCount() - 1
* @return The button text as a resource ID
*/
public abstract int getButtonText(int index);
public Integer getDefaultButtonID() {
return null;
}
/**
* Execute the action which corresponds to the nth button.
*
* @param index The button that was clicked.
*/
public abstract void handleButtonPress(int index);
/**
* Some barcode contents are considered secure, and should not be saved to history, copied to
* the clipboard, or otherwise persisted.
*
* @return If true, do not create any permanent record of these contents.
*/
public boolean areContentsSecure() {
return false;
}
/**
* Create a possibly styled string for the contents of the current barcode.
*
* @return The text to be displayed.
*/
public CharSequence getDisplayContents() {
String contents = result.getDisplayResult();
return contents.replace("\r", "");
}
/**
* A string describing the kind of barcode that was found, e.g. "Found contact info".
*
* @return The resource ID of the string.
*/
public abstract int getDisplayTitle();
/**
* A convenience method to get the parsed type. Should not be overridden.
*
* @return The parsed type, e.g. URI or ISBN
*/
public final ParsedResultType getType() {
return result.getType();
}
final void addPhoneOnlyContact(String[] phoneNumbers, String[] phoneTypes) {
addContact(null, null, null, phoneNumbers, phoneTypes, null, null, null, null, null, null, null, null, null, null, null);
}
final void addEmailOnlyContact(String[] emails, String[] emailTypes) {
addContact(null, null, null, null, null, emails, emailTypes, null, null, null, null, null, null, null, null, null);
}
final void addContact(String[] names,
String[] nicknames,
String pronunciation,
String[] phoneNumbers,
String[] phoneTypes,
String[] emails,
String[] emailTypes,
String note,
String instantMessenger,
String address,
String addressType,
String org,
String title,
String[] urls,
String birthday,
String[] geo) {
// Only use the first name in the array, if present.
Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT, ContactsContract.Contacts.CONTENT_URI);
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
putExtra(intent, ContactsContract.Intents.Insert.NAME, names != null ? names[0] : null);
putExtra(intent, ContactsContract.Intents.Insert.PHONETIC_NAME, pronunciation);
int phoneCount = Math.min(phoneNumbers != null ? phoneNumbers.length : 0, Contents.PHONE_KEYS.length);
for (int x = 0; x < phoneCount; x++) {
putExtra(intent, Contents.PHONE_KEYS[x], phoneNumbers[x]);
if (phoneTypes != null && x < phoneTypes.length) {
int type = toPhoneContractType(phoneTypes[x]);
if (type >= 0) {
intent.putExtra(Contents.PHONE_TYPE_KEYS[x], type);
}
}
}
int emailCount = Math.min(emails != null ? emails.length : 0, Contents.EMAIL_KEYS.length);
for (int x = 0; x < emailCount; x++) {
putExtra(intent, Contents.EMAIL_KEYS[x], emails[x]);
if (emailTypes != null && x < emailTypes.length) {
int type = toEmailContractType(emailTypes[x]);
if (type >= 0) {
intent.putExtra(Contents.EMAIL_TYPE_KEYS[x], type);
}
}
}
ArrayList<ContentValues> data = new ArrayList<ContentValues>();
if (urls != null) {
for (String url : urls) {
if (url != null && !url.isEmpty()) {
ContentValues row = new ContentValues(2);
row.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
row.put(ContactsContract.CommonDataKinds.Website.URL, url);
data.add(row);
break;
}
}
}
if (birthday != null) {
ContentValues row = new ContentValues(3);
row.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
row.put(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY);
row.put(ContactsContract.CommonDataKinds.Event.START_DATE, birthday);
data.add(row);
}
if (nicknames != null) {
for (String nickname : nicknames) {
if (nickname != null && !nickname.isEmpty()) {
ContentValues row = new ContentValues(3);
row.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
row.put(ContactsContract.CommonDataKinds.Nickname.TYPE,
ContactsContract.CommonDataKinds.Nickname.TYPE_DEFAULT);
row.put(ContactsContract.CommonDataKinds.Nickname.NAME, nickname);
data.add(row);
break;
}
}
}
if (!data.isEmpty()) {
intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, data);
}
StringBuilder aggregatedNotes = new StringBuilder();
if (note != null) {
aggregatedNotes.append('\n').append(note);
}
if (geo != null) {
aggregatedNotes.append('\n').append(geo[0]).append(',').append(geo[1]);
}
if (aggregatedNotes.length() > 0) {
// Remove extra leading '\n'
putExtra(intent, ContactsContract.Intents.Insert.NOTES, aggregatedNotes.substring(1));
}
putExtra(intent, ContactsContract.Intents.Insert.IM_HANDLE, instantMessenger);
putExtra(intent, ContactsContract.Intents.Insert.POSTAL, address);
if (addressType != null) {
int type = toAddressContractType(addressType);
if (type >= 0) {
intent.putExtra(ContactsContract.Intents.Insert.POSTAL_TYPE, type);
}
}
putExtra(intent, ContactsContract.Intents.Insert.COMPANY, org);
putExtra(intent, ContactsContract.Intents.Insert.JOB_TITLE, title);
launchIntent(intent);
}
private static int toEmailContractType(String typeString) {
return doToContractType(typeString, EMAIL_TYPE_STRINGS, EMAIL_TYPE_VALUES);
}
private static int toPhoneContractType(String typeString) {
return doToContractType(typeString, PHONE_TYPE_STRINGS, PHONE_TYPE_VALUES);
}
private static int toAddressContractType(String typeString) {
return doToContractType(typeString, ADDRESS_TYPE_STRINGS, ADDRESS_TYPE_VALUES);
}
private static int doToContractType(String typeString, String[] types, int[] values) {
if (typeString == null) {
return NO_TYPE;
}
for (int i = 0; i < types.length; i++) {
String type = types[i];
if (typeString.startsWith(type) || typeString.startsWith(type.toUpperCase(Locale.ENGLISH))) {
return values[i];
}
}
return NO_TYPE;
}
final void shareByEmail(String contents) {
sendEmail(null, null, null, null, contents);
}
final void sendEmail(String[] to,
String[] cc,
String[] bcc,
String subject,
String body) {
Intent intent = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));
if (to != null && to.length != 0) {
intent.putExtra(Intent.EXTRA_EMAIL, to);
}
if (cc != null && cc.length != 0) {
intent.putExtra(Intent.EXTRA_CC, cc);
}
if (bcc != null && bcc.length != 0) {
intent.putExtra(Intent.EXTRA_BCC, bcc);
}
putExtra(intent, Intent.EXTRA_SUBJECT, subject);
putExtra(intent, Intent.EXTRA_TEXT, body);
intent.setType("text/plain");
launchIntent(intent);
}
final void shareBySMS(String contents) {
sendSMSFromUri("smsto:", contents);
}
final void sendSMS(String phoneNumber, String body) {
sendSMSFromUri("smsto:" + phoneNumber, body);
}
final void sendSMSFromUri(String uri, String body) {
Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(uri));
putExtra(intent, "sms_body", body);
// Exit the app once the SMS is sent
intent.putExtra("compose_mode", true);
launchIntent(intent);
}
final void sendMMS(String phoneNumber, String subject, String body) {
sendMMSFromUri("mmsto:" + phoneNumber, subject, body);
}
final void sendMMSFromUri(String uri, String subject, String body) {
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.isEmpty()) {
putExtra(intent, "subject", activity.getString(R.string.msg_default_mms_subject));
} else {
putExtra(intent, "subject", subject);
}
putExtra(intent, "sms_body", body);
intent.putExtra("compose_mode", true);
launchIntent(intent);
}
final void dialPhone(String phoneNumber) {
launchIntent(new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber)));
}
final void dialPhoneFromUri(String uri) {
launchIntent(new Intent(Intent.ACTION_DIAL, Uri.parse(uri)));
}
final void openMap(String geoURI) {
launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(geoURI)));
}
/**
* Do a geo search using the address as the query.
*
* @param address The address to find
*/
final void searchMap(String address) {
launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=" + Uri.encode(address))));
}
final void getDirections(double latitude, double longitude) {
launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://maps.google." +
LocaleManager.getCountryTLD(activity) + "/maps?f=d&daddr=" + latitude + ',' + longitude)));
}
// Uses the mobile-specific version of Product Search, which is formatted for small screens.
final void openProductSearch(String upc) {
Uri uri = Uri.parse("http://www.google." + LocaleManager.getProductSearchCountryTLD(activity) +
"/m/products?q=" + upc + "&source=zxing");
launchIntent(new Intent(Intent.ACTION_VIEW, uri));
}
final void openBookSearch(String isbn) {
Uri uri = Uri.parse("http://books.google." + LocaleManager.getBookSearchCountryTLD(activity) +
"/books?vid=isbn" + isbn);
launchIntent(new Intent(Intent.ACTION_VIEW, uri));
}
final void searchBookContents(String isbnOrUrl) {
/* Intent intent = new Intent(Intents.SearchBookContents.ACTION);
intent.setClassName(activity, SearchBookContentsActivity.class.getName());
putExtra(intent, Intents.SearchBookContents.ISBN, isbnOrUrl);
launchIntent(intent);*/
Log.w("WBJ", "=========================searchBookContents()"+isbnOrUrl);
}
final void openURL(String url) {
// Strangely, some Android browsers don't seem to register to handle HTTP:// or HTTPS://.
// Lower-case these as it should always be OK to lower-case these schemes.
if (url.startsWith("HTTP://")) {
url = "http" + url.substring(4);
} else if (url.startsWith("HTTPS://")) {
url = "https" + url.substring(5);
}
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
try {
launchIntent(intent);
} catch (ActivityNotFoundException ignored) {
Log.w(TAG, "Nothing available to handle " + intent);
}
}
final void webSearch(String query) {
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.putExtra("query", query);
launchIntent(intent);
}
/**
* Like {@link #launchIntent(Intent)} but will tell you if it is not handle-able
* via {@link ActivityNotFoundException}.
*
* @throws ActivityNotFoundException
*/
final void rawLaunchIntent(Intent intent) {
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
Log.d(TAG, "Launching intent: " + intent + " with extras: " + intent.getExtras());
activity.startActivity(intent);
}
}
/**
* Like {@link #rawLaunchIntent(Intent)} but will show a user dialog if nothing is available to handle.
*/
final void launchIntent(Intent intent) {
try {
rawLaunchIntent(intent);
} catch (ActivityNotFoundException ignored) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(R.string.app_name);
builder.setMessage(R.string.msg_intent_failed);
builder.setPositiveButton(R.string.button_ok, null);
builder.show();
}
}
private static void putExtra(Intent intent, String key, String value) {
if (value != null && !value.isEmpty()) {
intent.putExtra(key, value);
}
}
private String parseCustomSearchURL() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
String customProductSearch = prefs.getString(PreferencesActivity.KEY_CUSTOM_PRODUCT_SEARCH,
null);
if (customProductSearch != null && customProductSearch.trim().isEmpty()) {
return null;
}
return customProductSearch;
}
final String fillInCustomSearchURL(String text) {
if (customProductSearch == null) {
return text; // ?
}
try {
text = URLEncoder.encode(text, "UTF-8");
} catch (UnsupportedEncodingException e) {
// can't happen; UTF-8 is always supported. Continue, I guess, without encoding
}
String url = customProductSearch;
if (rawResult != null) {
// Replace %f but only if it doesn't seem to be a hex escape sequence. This remains
// problematic but avoids the more surprising problem of breaking escapes
url = url.replaceFirst("%f(?![0-9a-f])", rawResult.getBarcodeFormat().toString());
if (url.contains("%t")) {
ParsedResult parsedResultAgain = ResultParser.parseResult(rawResult);
url = url.replace("%t", parsedResultAgain.getType().toString());
}
}
// Replace %s last as it might contain itself %f or %t
return url.replace("%s", text);
}
}
/*
* 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 com.google.zxing.Result;
import com.google.zxing.client.android.CaptureActivity;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ResultParser;
/**
* Manufactures Android-specific handlers based on the barcode content's type.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class ResultHandlerFactory {
private ResultHandlerFactory() {
}
public static ResultHandler makeResultHandler(CaptureActivity activity, Result rawResult) {
ParsedResult result = parseResult(rawResult);
switch (result.getType()) {
case ADDRESSBOOK:
return new AddressBookResultHandler(activity, result);
case EMAIL_ADDRESS:
return new EmailAddressResultHandler(activity, result);
case PRODUCT:
return new ProductResultHandler(activity, result, rawResult);
/* case URI:
return new URIResultHandler(activity, result);
case WIFI:
return new WifiResultHandler(activity, result);*/
case GEO:
return new GeoResultHandler(activity, result);
/*case TEL:
return new TelResultHandler(activity, result);
case SMS:
return new SMSResultHandler(activity, result);*/
case CALENDAR:
return new CalendarResultHandler(activity, result);
case ISBN:
return new ISBNResultHandler(activity, result, rawResult);
default:
return new TextResultHandler(activity, result, rawResult);
}
}
private static ParsedResult parseResult(Result rawResult) {
return ResultParser.parseResult(rawResult);
}
}
/*
* 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.app.Activity;
import com.google.zxing.Result;
import com.google.zxing.client.result.ParsedResult;
import com.senthink.ebike_administrer_tool.R;
/**
* 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[] buttons = {
R.string.button_web_search,
R.string.button_share_by_email,
R.string.button_share_by_sms,
R.string.button_custom_product_search,
};
public TextResultHandler(Activity activity, ParsedResult result, Result rawResult) {
super(activity, result, rawResult);
}
@Override
public int getButtonCount() {
return hasCustomProductSearch() ? buttons.length : buttons.length - 1;
}
@Override
public int getButtonText(int index) {
return buttons[index];
}
@Override
public void handleButtonPress(int index) {
String text = getResult().getDisplayResult();
switch (index) {
case 0:
webSearch(text);
break;
case 1:
shareByEmail(text);
break;
case 2:
shareBySMS(text);
break;
case 3:
openURL(fillInCustomSearchURL(text));
break;
}
}
@Override
public int getDisplayTitle() {
return R.string.result_text;
}
}
package com.senthink.ebike_administrer_tool.fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.google.zxing.client.android.CaptureActivity;
import com.senthink.ebike_administrer_tool.R;
import com.senthink.ebike_administrer_tool.adapter.DispatchOutAdapter;
import com.senthink.ebike_administrer_tool.bean.dispatch.Dispatchout;
import com.senthink.ebike_administrer_tool.common.Common;
import com.senthink.ebike_administrer_tool.presenter.impl.DIspatchOutPresenterImpl;
import com.senthink.ebike_administrer_tool.view.IDispatchFragment;
......@@ -24,6 +28,7 @@ import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* @Description : 调出车辆
* @Author : ni
......@@ -39,8 +44,10 @@ public class DispatchOutFragment extends Fragment implements IDispatchFragment {
DispatchOutAdapter mAdapter;
DIspatchOutPresenterImpl presenter;
private static final String TAG = DispatchFragment.class.getSimpleName();
private Dispatchout out = new Dispatchout();
private List<Dispatchout> mList = new ArrayList<>();
private static final int ACTION_SCAN_BARCODE = 1001;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
......@@ -68,7 +75,6 @@ public class DispatchOutFragment extends Fragment implements IDispatchFragment {
private void initData() {
presenter = new DIspatchOutPresenterImpl(this);
presenter.getCarInfo();
LinearLayoutManager layout = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layout);
......@@ -89,24 +95,34 @@ public class DispatchOutFragment extends Fragment implements IDispatchFragment {
@Override
public void scan() {
out.setParkId("123");
out.setCarId("3123");
mList.add(out);
// mAdapter.setData(mList);
mAdapter.notifyItemInserted(mList.size()-1);
mAdapter.notifyItemRangeChanged(mList.size()-1,mList.size()-1);
// out.setParkId("123");
// out.setCarId("3123");
// mList.add(out);
//// mAdapter.setData(mList);
// mAdapter.notifyItemInserted(mList.size()-1);
// mAdapter.notifyItemRangeChanged(mList.size()-1,mList.size()-1);
Intent intent = new Intent();
intent.setClass(getActivity(), CaptureActivity.class);
startActivityForResult(intent, ACTION_SCAN_BARCODE);
}
@Override
public void updateInfo() {
// TODO: 2017/6/12 获取数据
presenter.getCarInfo();
Log.i(TAG, "updateInfo: ");
}
@Override
public void submitSuccess() {
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_SCAN_BARCODE
&& resultCode == 1070) {
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:bottomLeftRadius="@dimen/switch_button_corners_radius"
android:bottomRightRadius="@dimen/switch_button_corners_radius"
android:topLeftRadius="@dimen/switch_button_corners_radius"
android:topRightRadius="@dimen/switch_button_corners_radius"/>
<padding android:left="5dp"/>
<solid android:color="@color/white"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:background="@color/background_color_white">
<LinearLayout
android:paddingTop="15dp"
......@@ -41,8 +42,7 @@
</LinearLayout>
<android.support.v4.view.ViewPager
android:id="@+id/vp_dispatch"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v4.view.ViewPager>
......
......@@ -2,10 +2,9 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingBottom="10dp">
android:paddingLeft="20dp"
android:paddingRight="20dp"
>
<EditText
android:id="@+id/et_input_parkname"
......@@ -13,7 +12,7 @@
android:textSize="@dimen/small_text"
android:layout_width="match_parent"
android:layout_height="37dp"
android:background="@drawable/modify_input_frame" />
android:background="@drawable/search_park_name" />
<RelativeLayout
android:id="@+id/rl_text"
android:paddingTop="10dp"
......@@ -65,7 +64,10 @@
<android.support.v7.widget.RecyclerView
android:layout_below="@+id/rl_text"
android:background="@drawable/search_park_name"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_above="@+id/btn_dispatch_in_scan"
android:id="@+id/recycleView_dispatch_in"
android:layout_width="match_parent"
android:layout_height="wrap_content"></android.support.v7.widget.RecyclerView>
......
......@@ -4,10 +4,9 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingBottom="10dp">
android:paddingLeft="20dp"
android:paddingRight="20dp"
>
<LinearLayout
android:id="@+id/ll_text"
android:orientation="horizontal"
......@@ -16,15 +15,21 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="@dimen/toolbar_submit_size"
android:textColor="@color/tab_tv_normal"
android:text="共"/>
<TextView
android:id="@+id/tv_dispatch_out_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/toolbar_submit_size"
android:textColor="@color/tab_tv_normal"
android:text="10"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="@dimen/toolbar_submit_size"
android:textColor="@color/tab_tv_normal"
android:text="辆"/>
</LinearLayout>
......@@ -35,7 +40,11 @@
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content">-->
<android.support.v7.widget.RecyclerView
android:background="@drawable/search_park_name"
android:layout_below="@+id/ll_text"
android:layout_above="@+id/btn_dispatch_out_scan"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:id="@+id/recycleView_dispatch_out"
android:layout_width="match_parent"
android:layout_height="wrap_content"></android.support.v7.widget.RecyclerView>
......
<?xml version="1.0" encoding="UTF-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<!-- 整体透明画布 -->
<SurfaceView
android:id="@+id/preview_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<!-- 扫描取景框 -->
<com.google.zxing.client.android.ViewfinderView
android:id="@+id/viewfinder_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<!-- android:background="@drawable/telbook_back" -->
<RelativeLayout
android:id="@+id/capture_header_rl"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_gravity="top"
android:background="#33000000"
>
<ImageButton
android:id="@+id/capture_imageview_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="10dip"
android:background="@drawable/icon_fh"
android:contentDescription="@string/app_name"/>
<TextView
android:id="@+id/capture_title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/logistics_barcode_title"
android:textSize="@dimen/txtSize_title_medium"
android:textColor="#ffffff"/>
</RelativeLayout>
</merge>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<string-array name="country_codes" tools:ignore="MissingTranslation">
<item>-</item>
<item>AR</item>
<item>AU</item>
<item>BR</item>
<item>BG</item>
<item>CA</item>
<item>CH</item>
<item>CN</item>
<item>CZ</item>
<item>DE</item>
<item>DK</item>
<item>ES</item>
<item>FI</item>
<item>FR</item>
<item>GB</item>
<item>GR</item>
<item>HU</item>
<item>ID</item>
<item>IT</item>
<item>JP</item>
<item>KR</item>
<item>NL</item>
<item>PL</item>
<item>PT</item>
<item>RO</item>
<item>RU</item>
<item>SE</item>
<item>SK</item>
<item>SI</item>
<item>TR</item>
<item>TW</item>
<item>US</item>
</string-array>
<string-array name="preferences_front_light_values" tools:ignore="MissingTranslation">
<item>ON</item>
<item>AUTO</item>
<item>OFF</item>
</string-array>
<string-array name="preferences_front_light_options">
<item>@string/preferences_front_light_on</item>
<item>@string/preferences_front_light_auto</item>
<item>@string/preferences_front_light_off</item>
</string-array>
</resources>
......@@ -16,4 +16,12 @@
<color name="background_color_grey">#f1f1f1</color>
<color name="background_color_white">#e2e2e2</color>
<color name="dispatch_park">#ACACAD</color>
<!--zxing二维码颜色-->
<color name="seperator_line">#e2e2e2</color>
<color name="viewfinder_mask">#60000000</color>
<color name="result_view">#b0000000</color>
<color name="viewfinder_laser">#ffff0000</color>
<color name="possible_result_points">#c0ffff00</color>
<color name="white">#ffffff</color>
</resources>
......@@ -10,4 +10,5 @@
<dimen name="dispatch_button_height">40dp</dimen>
<dimen name="switch_button_corners_radius">5dp</dimen>
<dimen name="stroke_width">1dp</dimen>
<dimen name="txtSize_title_medium">18sp</dimen>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<resources>
<!-- Messages IDs -->
<item type="id" name="auto_focus"/>
<item type="id" name="decode"/>
<item type="id" name="decode_failed"/>
<item type="id" name="decode_succeeded"/>
<item type="id" name="encode_failed"/>
<item type="id" name="encode_succeeded"/>
<item type="id" name="launch_product_query"/>
<item type="id" name="quit"/>
<item type="id" name="restart_preview"/>
<item type="id" name="return_scan_result"/>
<item type="id" name="search_book_contents_failed"/>
<item type="id" name="search_book_contents_succeeded"/>
</resources>
......@@ -30,4 +30,84 @@
<string name="input_park_name">输入站点名称</string>
<string name="limit_car_num">额定车位数:</string>
<string name="max_car_num">最多停车数:</string>
<!--zxing所用字符串-->
<string name="logistics_barcode_title">扫描车辆</string>
<string name="preferences_scanning_title">条码类型</string>
<string name="preferences_actions_title">扫描成功</string>
<string name="preferences_auto_focus_title">自动对焦</string>
<string name="preferences_auto_open_web_title">自动打开网页</string>
<string name="preferences_bulk_mode_summary">连续扫描并保存多个条码</string>
<string name="preferences_bulk_mode_title">批量扫描模式</string>
<string name="preferences_copy_to_clipboard_title">复制到剪贴板</string>
<string formatted="false" name="preferences_custom_product_search_summary">替换:%s=内容,%f=格式,%t=类型</string>
<string name="preferences_custom_product_search_title">自定义搜索网址</string>
<string name="preferences_decode_1D_industrial_title">一维码:工业</string>
<string name="preferences_decode_1D_product_title">一维码:商品</string>
<string name="preferences_decode_Aztec_title">Aztec</string>
<string name="preferences_decode_Data_Matrix_title">Data Matrix</string>
<string name="preferences_decode_PDF417_title">PDF417 (测试)</string>
<string name="preferences_decode_QR_title">二维码</string>
<string name="preferences_device_bug_workarounds_title">设备适配</string>
<string name="preferences_disable_barcode_scene_mode_title">不进行条形码场景匹配</string>
<string name="preferences_disable_continuous_focus_summary">使用标准对焦模式</string>
<string name="preferences_disable_continuous_focus_title">不持续对焦</string>
<string name="preferences_disable_exposure_title">不曝光</string>
<string name="preferences_disable_metering_title">不使用距离测量</string>
<string name="preferences_play_beep_title">播放提示音</string>
<string name="preferences_remember_duplicates_summary">在历史记录中保存重复的记录</string>
<string name="preferences_remember_duplicates_title">保存重复记录</string>
<string name="preferences_search_country">搜索引擎国别</string>
<string name="preferences_supplemental_summary">尝试检索关于条码内容的更多信息</string>
<string name="preferences_supplemental_title">检索更多信息</string>
<string name="preferences_vibrate_title">振动</string>
<string name="preferences_front_light_auto">自动</string>
<string name="preferences_front_light_off">关</string>
<string name="preferences_front_light_on">开</string>
<string name="preferences_result_title">sj</string>
<string name="preferences_orientation_title">不自动旋转</string>
<string name="preferences_invert_scan_summary">扫描黑色背景上的白色条码。仅适用于部分设备。</string>
<string name="preferences_invert_scan_title">反色</string>
<string name="preferences_front_light_summary">设置闪光灯模式</string>
<string name="preferences_front_light_title">闪光灯</string>
<string name="preferences_general_title">扫描设置</string>
<string name="preferences_history_summary">自动将条码存入历史记录</string>
<string name="preferences_history_title">存入历史记录</string>
<string name="msg_camera_framework_bug">Sorry, the Android camera encountered a problem. You may need to restart the device.</string>
<string name="msg_default_mms_subject">Hi</string>
<string name="msg_intent_failed">抱歉,无法打开应用。条码内容可能无效。</string>
<string name="button_ok">OK</string>
<string name="contents_text">纯文本</string>
<string name="contents_contact">联系人信息</string>
<string name="contents_email">E-mail 地址</string>
<string name="contents_location">经纬度</string>
<string name="contents_phone">电话号码</string>
<string name="contents_sms">短信号码</string>
<string name="button_add_contact">添加联系人</string>
<string name="button_show_map">显示地图</string>
<string name="button_dial">拨打电话</string>
<string name="button_email">发送 E-mail</string>
<string name="result_address_book">找到联系人信息</string>
<string name="button_add_calendar">添加至日程表</string>
<string name="result_calendar">找到日程</string>
<string name="result_email_address">找到电子邮件地址</string>
<string name="result_geo">找到地理坐标</string>
<string name="result_isbn">找到图书</string>
<string name="result_product">找到商品</string>
<string name="result_sms">找到短信号码</string>
<string name="result_tel">找到电话号码</string>
<string name="result_text">找到纯文本</string>
<string name="result_uri">找到 URL </string>
<string name="result_wifi">找到 Wi-Fi 配置</string>
<string name="button_get_directions">获取地址</string>
<string name="button_product_search">打开商品搜索</string>
<string name="button_search_book_contents">搜索图书内容</string>
<string name="button_share_app">分享应用</string>
<string name="button_share_bookmark">分享书签</string>
<string name="button_book_search">打开图书搜索</string>
<string name="button_custom_product_search">自定义搜索</string>
<string name="button_web_search">网页搜索</string>
<string name="button_share_by_email">通过 E-mail 分享</string>
<string name="button_share_by_sms">通过短信分享</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/preferences_scanning_title">
<CheckBoxPreference
android:key="preferences_decode_1D_product"
android:defaultValue="true"
android:title="@string/preferences_decode_1D_product_title"/>
<CheckBoxPreference
android:key="preferences_decode_1D_industrial"
android:defaultValue="true"
android:title="@string/preferences_decode_1D_industrial_title"/>
<CheckBoxPreference
android:key="preferences_decode_QR"
android:defaultValue="true"
android:title="@string/preferences_decode_QR_title"/>
<CheckBoxPreference
android:key="preferences_decode_Data_Matrix"
android:defaultValue="true"
android:title="@string/preferences_decode_Data_Matrix_title"/>
<CheckBoxPreference
android:key="preferences_decode_Aztec"
android:defaultValue="false"
android:title="@string/preferences_decode_Aztec_title"/>
<CheckBoxPreference
android:key="preferences_decode_PDF417"
android:defaultValue="false"
android:title="@string/preferences_decode_PDF417_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences_actions_title">
<CheckBoxPreference
android:key="preferences_play_beep"
android:defaultValue="true"
android:title="@string/preferences_play_beep_title"/>
<CheckBoxPreference
android:key="preferences_vibrate"
android:defaultValue="false"
android:title="@string/preferences_vibrate_title"/>
<CheckBoxPreference
android:key="preferences_copy_to_clipboard"
android:defaultValue="true"
android:title="@string/preferences_copy_to_clipboard_title"/>
<CheckBoxPreference
android:key="preferences_auto_open_web"
android:defaultValue="false"
android:title="@string/preferences_auto_open_web_title"/>
<CheckBoxPreference
android:key="preferences_remember_duplicates"
android:defaultValue="false"
android:title="@string/preferences_remember_duplicates_title"
android:summary="@string/preferences_remember_duplicates_summary"/>
<CheckBoxPreference
android:key="preferences_history"
android:defaultValue="true"
android:title="@string/preferences_history_title"
android:summary="@string/preferences_history_summary"/>
<CheckBoxPreference
android:key="preferences_supplemental"
android:defaultValue="true"
android:title="@string/preferences_supplemental_title"
android:summary="@string/preferences_supplemental_summary"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences_general_title">
<ListPreference
android:entries="@array/preferences_front_light_options"
android:entryValues="@array/preferences_front_light_values"
android:key="preferences_front_light_mode"
android:defaultValue="OFF"
android:title="@string/preferences_front_light_title"
android:summary="@string/preferences_front_light_summary"/>
<CheckBoxPreference
android:key="preferences_auto_focus"
android:defaultValue="true"
android:title="@string/preferences_auto_focus_title"/>
<CheckBoxPreference
android:key="preferences_invert_scan"
android:defaultValue="false"
android:title="@string/preferences_invert_scan_title"
android:summary="@string/preferences_invert_scan_summary"/>
<CheckBoxPreference
android:key="preferences_bulk_mode"
android:defaultValue="false"
android:title="@string/preferences_bulk_mode_title"
android:summary="@string/preferences_bulk_mode_summary"/>
<CheckBoxPreference
android:key="preferences_orientation"
android:defaultValue="true"
android:title="@string/preferences_orientation_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences_result_title">
<EditTextPreference
android:key="preferences_custom_product_search"
android:title="@string/preferences_custom_product_search_title"
android:summary="@string/preferences_custom_product_search_summary"/>
<ListPreference
android:key="preferences_search_country"
android:defaultValue="-"
android:entries="@array/country_codes"
android:entryValues="@array/country_codes"
android:title="@string/preferences_search_country"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences_device_bug_workarounds_title">
<CheckBoxPreference
android:key="preferences_disable_continuous_focus"
android:defaultValue="true"
android:title="@string/preferences_disable_continuous_focus_title"
android:summary="@string/preferences_disable_continuous_focus_summary"/>
<CheckBoxPreference
android:key="preferences_disable_exposure"
android:defaultValue="true"
android:title="@string/preferences_disable_exposure_title"/>
<CheckBoxPreference
android:key="preferences_disable_metering"
android:defaultValue="true"
android:title="@string/preferences_disable_metering_title"/>
<CheckBoxPreference
android:key="preferences_disable_barcode_scene_mode"
android:defaultValue="true"
android:title="@string/preferences_disable_barcode_scene_mode_title"/>
</PreferenceCategory>
</PreferenceScreen>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment