mirror of
https://github.com/jfoucry/Pilldroid.git
synced 2024-11-17 18:41:37 +01:00
Ajout des éléments nécessaire à ZXing
This commit is contained in:
parent
0f31419ec9
commit
b19edd2fbf
103 changed files with 11144 additions and 15 deletions
|
@ -33,8 +33,4 @@ dependencies {
|
||||||
compile 'com.android.support:support-v4:23.3.0'
|
compile 'com.android.support:support-v4:23.3.0'
|
||||||
compile 'com.android.support:recyclerview-v7:23.3.0'
|
compile 'com.android.support:recyclerview-v7:23.3.0'
|
||||||
compile 'com.android.support:design:23.3.0'
|
compile 'com.android.support:design:23.3.0'
|
||||||
compile 'com.embarkmobile:zxing-android-minimal:2.0@aar'
|
|
||||||
compile 'com.embarkmobile:zxing-android-legacy:2.0.0@aar'
|
|
||||||
compile 'com.embarkmobile:zxing-android-integration:2.0.0@aar'
|
|
||||||
compile 'com.google.zxing:core:3.0.1'
|
|
||||||
}
|
}
|
||||||
|
|
BIN
app/libs/android-core-3.2.2-SNAPSHOT.jar
Normal file
BIN
app/libs/android-core-3.2.2-SNAPSHOT.jar
Normal file
Binary file not shown.
BIN
app/libs/core-3.2.2-SNAPSHOT.jar
Normal file
BIN
app/libs/core-3.2.2-SNAPSHOT.jar
Normal file
Binary file not shown.
|
@ -2,6 +2,8 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="net.foucry.pilldroid">
|
package="net.foucry.pilldroid">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
@ -27,6 +29,20 @@
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="net.foucry.pilldroid.MedicamentListActivity" />
|
android:value="net.foucry.pilldroid.MedicamentListActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity android:name="com.google.zxing.client.android.CaptureActivity"
|
||||||
|
android:screenOrientation="landscape"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||||
|
android:windowSoftInputMode="stateAlwaysHidden">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.zxing.client.android.SCAN"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,769 @@
|
||||||
|
/*
|
||||||
|
* 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 com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.DecodeHintType;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.ResultMetadataType;
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.client.android.camera.CameraManager;
|
||||||
|
import com.google.zxing.client.android.clipboard.ClipboardInterface;
|
||||||
|
import com.google.zxing.client.android.history.HistoryActivity;
|
||||||
|
import com.google.zxing.client.android.history.HistoryItem;
|
||||||
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
|
import com.google.zxing.client.android.result.ResultButtonListener;
|
||||||
|
import com.google.zxing.client.android.result.ResultHandler;
|
||||||
|
import com.google.zxing.client.android.result.ResultHandlerFactory;
|
||||||
|
import com.google.zxing.client.android.result.supplement.SupplementalInfoRetriever;
|
||||||
|
import com.google.zxing.client.android.share.ShareActivity;
|
||||||
|
|
||||||
|
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.BitmapFactory;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
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.util.TypedValue;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
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 long DEFAULT_INTENT_RESULT_DURATION_MS = 1500L;
|
||||||
|
private static final long BULK_MODE_SCAN_DELAY_MS = 1000L;
|
||||||
|
|
||||||
|
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 TextView statusView;
|
||||||
|
private View resultView;
|
||||||
|
private Result lastResult;
|
||||||
|
private boolean hasSurface;
|
||||||
|
private boolean copyToClipboard;
|
||||||
|
private IntentSource source;
|
||||||
|
private String sourceUrl;
|
||||||
|
private ScanFromWebPageManager scanFromWebPageManager;
|
||||||
|
private Collection<BarcodeFormat> decodeFormats;
|
||||||
|
private Map<DecodeHintType,?> decodeHints;
|
||||||
|
private String characterSet;
|
||||||
|
private HistoryManager historyManager;
|
||||||
|
private InactivityTimer inactivityTimer;
|
||||||
|
private BeepManager beepManager;
|
||||||
|
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.capture);
|
||||||
|
|
||||||
|
hasSurface = false;
|
||||||
|
inactivityTimer = new InactivityTimer(this);
|
||||||
|
beepManager = new BeepManager(this);
|
||||||
|
ambientLightManager = new AmbientLightManager(this);
|
||||||
|
|
||||||
|
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
// historyManager must be initialized here to update the history preference
|
||||||
|
historyManager = new HistoryManager(this);
|
||||||
|
historyManager.trimHistory();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
resultView = findViewById(R.id.result_view);
|
||||||
|
statusView = (TextView) findViewById(R.id.status_view);
|
||||||
|
|
||||||
|
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;
|
||||||
|
sourceUrl = null;
|
||||||
|
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;
|
||||||
|
sourceUrl = dataString;
|
||||||
|
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;
|
||||||
|
sourceUrl = dataString;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
MenuInflater menuInflater = getMenuInflater();
|
||||||
|
menuInflater.inflate(R.menu.capture, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_share:
|
||||||
|
intent.setClassName(this, ShareActivity.class.getName());
|
||||||
|
startActivity(intent);
|
||||||
|
break;
|
||||||
|
case R.id.menu_history:
|
||||||
|
intent.setClassName(this, HistoryActivity.class.getName());
|
||||||
|
startActivityForResult(intent, HISTORY_REQUEST_CODE);
|
||||||
|
break;
|
||||||
|
case R.id.menu_settings:
|
||||||
|
intent.setClassName(this, PreferencesActivity.class.getName());
|
||||||
|
startActivity(intent);
|
||||||
|
break;
|
||||||
|
case R.id.menu_help:
|
||||||
|
intent.setClassName(this, HelpActivity.class.getName());
|
||||||
|
startActivity(intent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
|
if (resultCode == RESULT_OK && requestCode == HISTORY_REQUEST_CODE && historyManager != null) {
|
||||||
|
int itemNumber = intent.getIntExtra(Intents.History.ITEM_NUMBER, -1);
|
||||||
|
if (itemNumber >= 0) {
|
||||||
|
HistoryItem historyItem = historyManager.buildHistoryItem(itemNumber);
|
||||||
|
decodeOrStoreSavedBitmap(null, historyItem.getResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
ResultHandler resultHandler = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (source) {
|
||||||
|
case NATIVE_APP_INTENT:
|
||||||
|
case PRODUCT_SEARCH_LINK:
|
||||||
|
handleDecodeExternally(rawResult, resultHandler, barcode);
|
||||||
|
break;
|
||||||
|
case ZXING_LINK:
|
||||||
|
if (scanFromWebPageManager == null || !scanFromWebPageManager.isScanFromWebPage()) {
|
||||||
|
handleDecodeInternally(rawResult, resultHandler, barcode);
|
||||||
|
} else {
|
||||||
|
handleDecodeExternally(rawResult, resultHandler, barcode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NONE:
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
if (fromLiveScan && prefs.getBoolean(PreferencesActivity.KEY_BULK_MODE, false)) {
|
||||||
|
Toast.makeText(getApplicationContext(),
|
||||||
|
getResources().getString(R.string.msg_bulk_mode_scanned) + " (" + rawResult.getText() + ')',
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
// Wait a moment or else it will scan the same barcode continuously about 3 times
|
||||||
|
restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS);
|
||||||
|
} else {
|
||||||
|
handleDecodeInternally(rawResult, resultHandler, barcode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Superimpose a line for 1D or dots for 2D to highlight the key features of the barcode.
|
||||||
|
*
|
||||||
|
* @param barcode A bitmap of the captured image.
|
||||||
|
* @param scaleFactor amount by which thumbnail was scaled
|
||||||
|
* @param rawResult The decoded results which contains the points to draw.
|
||||||
|
*/
|
||||||
|
private void drawResultPoints(Bitmap barcode, float scaleFactor, Result rawResult) {
|
||||||
|
ResultPoint[] points = rawResult.getResultPoints();
|
||||||
|
if (points != null && points.length > 0) {
|
||||||
|
Canvas canvas = new Canvas(barcode);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setColor(getResources().getColor(R.color.result_points));
|
||||||
|
if (points.length == 2) {
|
||||||
|
paint.setStrokeWidth(4.0f);
|
||||||
|
drawLine(canvas, paint, points[0], points[1], scaleFactor);
|
||||||
|
} else if (points.length == 4 &&
|
||||||
|
(rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A ||
|
||||||
|
rawResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
|
||||||
|
// Hacky special case -- draw two lines, for the barcode and metadata
|
||||||
|
drawLine(canvas, paint, points[0], points[1], scaleFactor);
|
||||||
|
drawLine(canvas, paint, points[2], points[3], scaleFactor);
|
||||||
|
} else {
|
||||||
|
paint.setStrokeWidth(10.0f);
|
||||||
|
for (ResultPoint point : points) {
|
||||||
|
if (point != null) {
|
||||||
|
canvas.drawPoint(scaleFactor * point.getX(), scaleFactor * point.getY(), paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void drawLine(Canvas canvas, Paint paint, ResultPoint a, ResultPoint b, float scaleFactor) {
|
||||||
|
if (a != null && b != null) {
|
||||||
|
canvas.drawLine(scaleFactor * a.getX(),
|
||||||
|
scaleFactor * a.getY(),
|
||||||
|
scaleFactor * b.getX(),
|
||||||
|
scaleFactor * b.getY(),
|
||||||
|
paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put up our own UI for how to handle the decoded contents.
|
||||||
|
private void handleDecodeInternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {
|
||||||
|
|
||||||
|
CharSequence displayContents = resultHandler.getDisplayContents();
|
||||||
|
|
||||||
|
if (copyToClipboard && !resultHandler.areContentsSecure()) {
|
||||||
|
ClipboardInterface.setText(displayContents, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
|
if (resultHandler.getDefaultButtonID() != null && prefs.getBoolean(PreferencesActivity.KEY_AUTO_OPEN_WEB, false)) {
|
||||||
|
resultHandler.handleButtonPress(resultHandler.getDefaultButtonID());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
statusView.setVisibility(View.GONE);
|
||||||
|
viewfinderView.setVisibility(View.GONE);
|
||||||
|
resultView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
ImageView barcodeImageView = (ImageView) findViewById(R.id.barcode_image_view);
|
||||||
|
if (barcode == null) {
|
||||||
|
barcodeImageView.setImageBitmap(BitmapFactory.decodeResource(getResources(),
|
||||||
|
R.mipmap.ic_launcher));
|
||||||
|
} else {
|
||||||
|
barcodeImageView.setImageBitmap(barcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
|
||||||
|
formatTextView.setText(rawResult.getBarcodeFormat().toString());
|
||||||
|
|
||||||
|
TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
|
||||||
|
typeTextView.setText(resultHandler.getType().toString());
|
||||||
|
|
||||||
|
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
|
||||||
|
TextView timeTextView = (TextView) findViewById(R.id.time_text_view);
|
||||||
|
timeTextView.setText(formatter.format(new Date(rawResult.getTimestamp())));
|
||||||
|
|
||||||
|
|
||||||
|
TextView metaTextView = (TextView) findViewById(R.id.meta_text_view);
|
||||||
|
View metaTextViewLabel = findViewById(R.id.meta_text_view_label);
|
||||||
|
metaTextView.setVisibility(View.GONE);
|
||||||
|
metaTextViewLabel.setVisibility(View.GONE);
|
||||||
|
Map<ResultMetadataType,Object> metadata = rawResult.getResultMetadata();
|
||||||
|
if (metadata != null) {
|
||||||
|
StringBuilder metadataText = new StringBuilder(20);
|
||||||
|
for (Map.Entry<ResultMetadataType,Object> entry : metadata.entrySet()) {
|
||||||
|
if (DISPLAYABLE_METADATA_TYPES.contains(entry.getKey())) {
|
||||||
|
metadataText.append(entry.getValue()).append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (metadataText.length() > 0) {
|
||||||
|
metadataText.setLength(metadataText.length() - 1);
|
||||||
|
metaTextView.setText(metadataText);
|
||||||
|
metaTextView.setVisibility(View.VISIBLE);
|
||||||
|
metaTextViewLabel.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
|
||||||
|
contentsTextView.setText(displayContents);
|
||||||
|
int scaledSize = Math.max(22, 32 - displayContents.length() / 4);
|
||||||
|
contentsTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, scaledSize);
|
||||||
|
|
||||||
|
TextView supplementTextView = (TextView) findViewById(R.id.contents_supplement_text_view);
|
||||||
|
supplementTextView.setText("");
|
||||||
|
supplementTextView.setOnClickListener(null);
|
||||||
|
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
|
||||||
|
PreferencesActivity.KEY_SUPPLEMENTAL, true)) {
|
||||||
|
SupplementalInfoRetriever.maybeInvokeRetrieval(supplementTextView,
|
||||||
|
resultHandler.getResult(),
|
||||||
|
historyManager,
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int buttonCount = resultHandler.getButtonCount();
|
||||||
|
ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
|
||||||
|
buttonView.requestFocus();
|
||||||
|
for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
|
||||||
|
TextView button = (TextView) buttonView.getChildAt(x);
|
||||||
|
if (x < buttonCount) {
|
||||||
|
button.setVisibility(View.VISIBLE);
|
||||||
|
button.setText(resultHandler.getButtonText(x));
|
||||||
|
button.setOnClickListener(new ResultButtonListener(resultHandler, x));
|
||||||
|
} else {
|
||||||
|
button.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Briefly show the contents of the barcode, then handle the result outside Barcode Scanner.
|
||||||
|
private void handleDecodeExternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {
|
||||||
|
|
||||||
|
if (barcode != null) {
|
||||||
|
viewfinderView.drawResultBitmap(barcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
long resultDurationMS;
|
||||||
|
if (getIntent() == null) {
|
||||||
|
resultDurationMS = DEFAULT_INTENT_RESULT_DURATION_MS;
|
||||||
|
} else {
|
||||||
|
resultDurationMS = getIntent().getLongExtra(Intents.Scan.RESULT_DISPLAY_DURATION_MS,
|
||||||
|
DEFAULT_INTENT_RESULT_DURATION_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultDurationMS > 0) {
|
||||||
|
String rawResultString = String.valueOf(rawResult);
|
||||||
|
if (rawResultString.length() > 32) {
|
||||||
|
rawResultString = rawResultString.substring(0, 32) + " ...";
|
||||||
|
}
|
||||||
|
statusView.setText(getString(resultHandler.getDisplayTitle()) + " : " + rawResultString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copyToClipboard && !resultHandler.areContentsSecure()) {
|
||||||
|
CharSequence text = resultHandler.getDisplayContents();
|
||||||
|
ClipboardInterface.setText(text, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source == IntentSource.NATIVE_APP_INTENT) {
|
||||||
|
|
||||||
|
// Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when
|
||||||
|
// the deprecated intent is retired.
|
||||||
|
Intent intent = new Intent(getIntent().getAction());
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
|
||||||
|
intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
|
||||||
|
byte[] rawBytes = rawResult.getRawBytes();
|
||||||
|
if (rawBytes != null && rawBytes.length > 0) {
|
||||||
|
intent.putExtra(Intents.Scan.RESULT_BYTES, rawBytes);
|
||||||
|
}
|
||||||
|
Map<ResultMetadataType,?> metadata = rawResult.getResultMetadata();
|
||||||
|
if (metadata != null) {
|
||||||
|
if (metadata.containsKey(ResultMetadataType.UPC_EAN_EXTENSION)) {
|
||||||
|
intent.putExtra(Intents.Scan.RESULT_UPC_EAN_EXTENSION,
|
||||||
|
metadata.get(ResultMetadataType.UPC_EAN_EXTENSION).toString());
|
||||||
|
}
|
||||||
|
Number orientation = (Number) metadata.get(ResultMetadataType.ORIENTATION);
|
||||||
|
if (orientation != null) {
|
||||||
|
intent.putExtra(Intents.Scan.RESULT_ORIENTATION, orientation.intValue());
|
||||||
|
}
|
||||||
|
String ecLevel = (String) metadata.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);
|
||||||
|
if (ecLevel != null) {
|
||||||
|
intent.putExtra(Intents.Scan.RESULT_ERROR_CORRECTION_LEVEL, ecLevel);
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Iterable<byte[]> byteSegments = (Iterable<byte[]>) metadata.get(ResultMetadataType.BYTE_SEGMENTS);
|
||||||
|
if (byteSegments != null) {
|
||||||
|
int i = 0;
|
||||||
|
for (byte[] byteSegment : byteSegments) {
|
||||||
|
intent.putExtra(Intents.Scan.RESULT_BYTE_SEGMENTS_PREFIX + i, byteSegment);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendReplyMessage(R.id.return_scan_result, intent, resultDurationMS);
|
||||||
|
|
||||||
|
} else if (source == IntentSource.PRODUCT_SEARCH_LINK) {
|
||||||
|
|
||||||
|
// Reformulate the URL which triggered us into a query, so that the request goes to the same
|
||||||
|
// TLD as the scan URL.
|
||||||
|
int end = sourceUrl.lastIndexOf("/scan");
|
||||||
|
String replyURL = sourceUrl.substring(0, end) + "?q=" + resultHandler.getDisplayContents() + "&source=zxing";
|
||||||
|
sendReplyMessage(R.id.launch_product_query, replyURL, resultDurationMS);
|
||||||
|
|
||||||
|
} else if (source == IntentSource.ZXING_LINK) {
|
||||||
|
|
||||||
|
if (scanFromWebPageManager != null && scanFromWebPageManager.isScanFromWebPage()) {
|
||||||
|
String replyURL = scanFromWebPageManager.buildReplyURL(rawResult, resultHandler);
|
||||||
|
scanFromWebPageManager = null;
|
||||||
|
sendReplyMessage(R.id.launch_product_query, replyURL, resultDurationMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendReplyMessage(int id, Object arg, long delayMS) {
|
||||||
|
if (handler != null) {
|
||||||
|
Message message = Message.obtain(handler, id, arg);
|
||||||
|
if (delayMS > 0L) {
|
||||||
|
handler.sendMessageDelayed(message, delayMS);
|
||||||
|
} else {
|
||||||
|
handler.sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(R.string.button_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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
* 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.ActivityNotFoundException;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.provider.Browser;
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.DecodeHintType;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.client.android.camera.CameraManager;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
115
app/src/main/java/com/google/zxing/client/android/Contents.java
Normal file
115
app/src/main/java/com/google/zxing/client/android/Contents.java
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* 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<>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,236 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.zxing.DecodeHintType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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<>();
|
||||||
|
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.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.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* 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 com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.DecodeHintType;
|
||||||
|
import com.google.zxing.ResultPointCallback;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
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.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 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;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An HTML-based help screen.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class HelpActivity extends Activity {
|
||||||
|
|
||||||
|
private static final String BASE_URL =
|
||||||
|
"file:///android_asset/html-" + LocaleManager.getTranslatedAssetLanguage() + '/';
|
||||||
|
|
||||||
|
private WebView webView;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle icicle) {
|
||||||
|
super.onCreate(icicle);
|
||||||
|
setContentView(R.layout.help);
|
||||||
|
|
||||||
|
webView = (WebView) findViewById(R.id.help_contents);
|
||||||
|
|
||||||
|
if (icicle == null) {
|
||||||
|
webView.loadUrl(BASE_URL + "index.html");
|
||||||
|
} else {
|
||||||
|
webView.restoreState(icicle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
|
||||||
|
webView.goBack();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(Bundle icicle) {
|
||||||
|
super.onSaveInstanceState(icicle);
|
||||||
|
webView.saveState(icicle);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
* 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<>(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 | 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 | IllegalArgumentException | IndexOutOfBoundsException | 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 | StringIndexOutOfBoundsException | IllegalArgumentException e) {
|
||||||
|
// this is maybe this Android bug: http://code.google.com/p/android/issues/detail?id=15554
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
}
|
278
app/src/main/java/com/google/zxing/client/android/Intents.java
Normal file
278
app/src/main/java/com/google/zxing/client/android/Intents.java
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
/*
|
||||||
|
* 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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* 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.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles any locale-specific logic for the client.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class LocaleManager {
|
||||||
|
|
||||||
|
private static final String DEFAULT_TLD = "com";
|
||||||
|
private static final 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<>();
|
||||||
|
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<>();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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<>(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(R.string.msg_error);
|
||||||
|
builder.setMessage(R.string.msg_invalid_value);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* 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.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.client.android.camera.CameraManager;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
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<>(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 {
|
||||||
|
|
||||||
|
// 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<>(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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.book;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import com.google.zxing.client.android.LocaleManager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class BrowseBookListener implements AdapterView.OnItemClickListener {
|
||||||
|
|
||||||
|
private final SearchBookContentsActivity activity;
|
||||||
|
private final List<SearchBookContentsResult> items;
|
||||||
|
|
||||||
|
BrowseBookListener(SearchBookContentsActivity activity, List<SearchBookContentsResult> items) {
|
||||||
|
this.activity = activity;
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||||
|
if (position < 1) {
|
||||||
|
// Clicked header, ignore it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int itemOffset = position - 1;
|
||||||
|
if (itemOffset >= items.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String pageId = items.get(itemOffset).getPageId();
|
||||||
|
String query = SearchBookContentsResult.getQuery();
|
||||||
|
if (LocaleManager.isBookSearchUrl(activity.getISBN()) && !pageId.isEmpty()) {
|
||||||
|
String uri = activity.getISBN();
|
||||||
|
int equals = uri.indexOf('=');
|
||||||
|
String volumeId = uri.substring(equals + 1);
|
||||||
|
String readBookURI = "http://books.google." +
|
||||||
|
LocaleManager.getBookSearchCountryTLD(activity) +
|
||||||
|
"/books?id=" + volumeId + "&pg=" + pageId + "&vq=" + query;
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(readBookURI));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,274 @@
|
||||||
|
/*
|
||||||
|
* 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.book;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.webkit.CookieManager;
|
||||||
|
import android.webkit.CookieSyncManager;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.zxing.client.android.HttpHelper;
|
||||||
|
import com.google.zxing.client.android.Intents;
|
||||||
|
import com.google.zxing.client.android.LocaleManager;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
/**
|
||||||
|
* Uses Google Book Search to find a word or phrase in the requested book.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class SearchBookContentsActivity extends Activity {
|
||||||
|
|
||||||
|
private static final String TAG = SearchBookContentsActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final Pattern TAG_PATTERN = Pattern.compile("\\<.*?\\>");
|
||||||
|
private static final Pattern LT_ENTITY_PATTERN = Pattern.compile("<");
|
||||||
|
private static final Pattern GT_ENTITY_PATTERN = Pattern.compile(">");
|
||||||
|
private static final Pattern QUOTE_ENTITY_PATTERN = Pattern.compile("'");
|
||||||
|
private static final Pattern QUOT_ENTITY_PATTERN = Pattern.compile(""");
|
||||||
|
|
||||||
|
private String isbn;
|
||||||
|
private EditText queryTextView;
|
||||||
|
private View queryButton;
|
||||||
|
private ListView resultListView;
|
||||||
|
private TextView headerView;
|
||||||
|
private AsyncTask<String,?,?> networkTask;
|
||||||
|
|
||||||
|
private final View.OnClickListener buttonListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
launchSearch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final View.OnKeyListener keyListener = new View.OnKeyListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View view, int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||||
|
launchSearch();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
String getISBN() {
|
||||||
|
return isbn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle icicle) {
|
||||||
|
super.onCreate(icicle);
|
||||||
|
|
||||||
|
// Make sure that expired cookies are removed on launch.
|
||||||
|
CookieSyncManager.createInstance(this);
|
||||||
|
CookieManager.getInstance().removeExpiredCookie();
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
if (intent == null || !Intents.SearchBookContents.ACTION.equals(intent.getAction())) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isbn = intent.getStringExtra(Intents.SearchBookContents.ISBN);
|
||||||
|
if (LocaleManager.isBookSearchUrl(isbn)) {
|
||||||
|
setTitle(getString(R.string.sbc_name));
|
||||||
|
} else {
|
||||||
|
setTitle(getString(R.string.sbc_name) + ": ISBN " + isbn);
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(R.layout.search_book_contents);
|
||||||
|
queryTextView = (EditText) findViewById(R.id.query_text_view);
|
||||||
|
|
||||||
|
String initialQuery = intent.getStringExtra(Intents.SearchBookContents.QUERY);
|
||||||
|
if (initialQuery != null && !initialQuery.isEmpty()) {
|
||||||
|
// Populate the search box but don't trigger the search
|
||||||
|
queryTextView.setText(initialQuery);
|
||||||
|
}
|
||||||
|
queryTextView.setOnKeyListener(keyListener);
|
||||||
|
|
||||||
|
queryButton = findViewById(R.id.query_button);
|
||||||
|
queryButton.setOnClickListener(buttonListener);
|
||||||
|
|
||||||
|
resultListView = (ListView) findViewById(R.id.result_list_view);
|
||||||
|
LayoutInflater factory = LayoutInflater.from(this);
|
||||||
|
headerView = (TextView) factory.inflate(R.layout.search_book_contents_header,
|
||||||
|
resultListView, false);
|
||||||
|
resultListView.addHeaderView(headerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
queryTextView.selectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
AsyncTask<?,?,?> oldTask = networkTask;
|
||||||
|
if (oldTask != null) {
|
||||||
|
oldTask.cancel(true);
|
||||||
|
networkTask = null;
|
||||||
|
}
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void launchSearch() {
|
||||||
|
String query = queryTextView.getText().toString();
|
||||||
|
if (query != null && !query.isEmpty()) {
|
||||||
|
AsyncTask<?,?,?> oldTask = networkTask;
|
||||||
|
if (oldTask != null) {
|
||||||
|
oldTask.cancel(true);
|
||||||
|
}
|
||||||
|
networkTask = new NetworkTask();
|
||||||
|
networkTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, query, isbn);
|
||||||
|
headerView.setText(R.string.msg_sbc_searching_book);
|
||||||
|
resultListView.setAdapter(null);
|
||||||
|
queryTextView.setEnabled(false);
|
||||||
|
queryButton.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class NetworkTask extends AsyncTask<String,Object,JSONObject> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSONObject doInBackground(String... args) {
|
||||||
|
try {
|
||||||
|
// These return a JSON result which describes if and where the query was found. This API may
|
||||||
|
// break or disappear at any time in the future. Since this is an API call rather than a
|
||||||
|
// website, we don't use LocaleManager to change the TLD.
|
||||||
|
String theQuery = args[0];
|
||||||
|
String theIsbn = args[1];
|
||||||
|
String uri;
|
||||||
|
if (LocaleManager.isBookSearchUrl(theIsbn)) {
|
||||||
|
int equals = theIsbn.indexOf('=');
|
||||||
|
String volumeId = theIsbn.substring(equals + 1);
|
||||||
|
uri = "http://www.google.com/books?id=" + volumeId + "&jscmd=SearchWithinVolume2&q=" + theQuery;
|
||||||
|
} else {
|
||||||
|
uri = "http://www.google.com/books?vid=isbn" + theIsbn + "&jscmd=SearchWithinVolume2&q=" + theQuery;
|
||||||
|
}
|
||||||
|
CharSequence content = HttpHelper.downloadViaHttp(uri, HttpHelper.ContentType.JSON);
|
||||||
|
return new JSONObject(content.toString());
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.w(TAG, "Error accessing book search", ioe);
|
||||||
|
return null;
|
||||||
|
} catch (JSONException je) {
|
||||||
|
Log.w(TAG, "Error accessing book search", je);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(JSONObject result) {
|
||||||
|
if (result == null) {
|
||||||
|
headerView.setText(R.string.msg_sbc_failed);
|
||||||
|
} else {
|
||||||
|
handleSearchResults(result);
|
||||||
|
}
|
||||||
|
queryTextView.setEnabled(true);
|
||||||
|
queryTextView.selectAll();
|
||||||
|
queryButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently there is no way to distinguish between a query which had no results and a book
|
||||||
|
// which is not searchable - both return zero results.
|
||||||
|
private void handleSearchResults(JSONObject json) {
|
||||||
|
try {
|
||||||
|
int count = json.getInt("number_of_results");
|
||||||
|
headerView.setText(getString(R.string.msg_sbc_results) + " : " + count);
|
||||||
|
if (count > 0) {
|
||||||
|
JSONArray results = json.getJSONArray("search_results");
|
||||||
|
SearchBookContentsResult.setQuery(queryTextView.getText().toString());
|
||||||
|
List<SearchBookContentsResult> items = new ArrayList<>(count);
|
||||||
|
for (int x = 0; x < count; x++) {
|
||||||
|
items.add(parseResult(results.getJSONObject(x)));
|
||||||
|
}
|
||||||
|
resultListView.setOnItemClickListener(new BrowseBookListener(SearchBookContentsActivity.this, items));
|
||||||
|
resultListView.setAdapter(new SearchBookContentsAdapter(SearchBookContentsActivity.this, items));
|
||||||
|
} else {
|
||||||
|
String searchable = json.optString("searchable");
|
||||||
|
if ("false".equals(searchable)) {
|
||||||
|
headerView.setText(R.string.msg_sbc_book_not_searchable);
|
||||||
|
}
|
||||||
|
resultListView.setAdapter(null);
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.w(TAG, "Bad JSON from book search", e);
|
||||||
|
resultListView.setAdapter(null);
|
||||||
|
headerView.setText(R.string.msg_sbc_failed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Available fields: page_id, page_number, snippet_text
|
||||||
|
private SearchBookContentsResult parseResult(JSONObject json) {
|
||||||
|
|
||||||
|
String pageId;
|
||||||
|
String pageNumber;
|
||||||
|
String snippet;
|
||||||
|
try {
|
||||||
|
pageId = json.getString("page_id");
|
||||||
|
pageNumber = json.optString("page_number");
|
||||||
|
snippet = json.optString("snippet_text");
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
// Never seen in the wild, just being complete.
|
||||||
|
return new SearchBookContentsResult(getString(R.string.msg_sbc_no_page_returned), "", "", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageNumber == null || pageNumber.isEmpty()) {
|
||||||
|
// This can happen for text on the jacket, and possibly other reasons.
|
||||||
|
pageNumber = "";
|
||||||
|
} else {
|
||||||
|
pageNumber = getString(R.string.msg_sbc_page) + ' ' + pageNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean valid = snippet != null && !snippet.isEmpty();
|
||||||
|
if (valid) {
|
||||||
|
// Remove all HTML tags and encoded characters.
|
||||||
|
snippet = TAG_PATTERN.matcher(snippet).replaceAll("");
|
||||||
|
snippet = LT_ENTITY_PATTERN.matcher(snippet).replaceAll("<");
|
||||||
|
snippet = GT_ENTITY_PATTERN.matcher(snippet).replaceAll(">");
|
||||||
|
snippet = QUOTE_ENTITY_PATTERN.matcher(snippet).replaceAll("'");
|
||||||
|
snippet = QUOT_ENTITY_PATTERN.matcher(snippet).replaceAll("\"");
|
||||||
|
} else {
|
||||||
|
snippet = '(' + getString(R.string.msg_sbc_snippet_unavailable) + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SearchBookContentsResult(pageId, pageNumber, snippet, valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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.book;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
/**
|
||||||
|
* Manufactures list items which represent SBC results.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
final class SearchBookContentsAdapter extends ArrayAdapter<SearchBookContentsResult> {
|
||||||
|
|
||||||
|
SearchBookContentsAdapter(Context context, List<SearchBookContentsResult> items) {
|
||||||
|
super(context, R.layout.search_book_contents_list_item, 0, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View view, ViewGroup viewGroup) {
|
||||||
|
SearchBookContentsListItem listItem;
|
||||||
|
|
||||||
|
if (view == null) {
|
||||||
|
LayoutInflater factory = LayoutInflater.from(getContext());
|
||||||
|
listItem = (SearchBookContentsListItem) factory.inflate(
|
||||||
|
R.layout.search_book_contents_list_item, viewGroup, false);
|
||||||
|
} else {
|
||||||
|
if (view instanceof SearchBookContentsListItem) {
|
||||||
|
listItem = (SearchBookContentsListItem) view;
|
||||||
|
} else {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchBookContentsResult result = getItem(position);
|
||||||
|
listItem.set(result);
|
||||||
|
return listItem;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* 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.book;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.style.StyleSpan;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list item which displays the page number and snippet of this search result.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class SearchBookContentsListItem extends LinearLayout {
|
||||||
|
private TextView pageNumberView;
|
||||||
|
private TextView snippetView;
|
||||||
|
|
||||||
|
SearchBookContentsListItem(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchBookContentsListItem(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
|
pageNumberView = (TextView) findViewById(R.id.page_number_view);
|
||||||
|
snippetView = (TextView) findViewById(R.id.snippet_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(SearchBookContentsResult result) {
|
||||||
|
pageNumberView.setText(result.getPageNumber());
|
||||||
|
String snippet = result.getSnippet();
|
||||||
|
if (snippet.isEmpty()) {
|
||||||
|
snippetView.setText("");
|
||||||
|
} else {
|
||||||
|
if (result.getValidSnippet()) {
|
||||||
|
String lowerQuery = SearchBookContentsResult.getQuery().toLowerCase(Locale.getDefault());
|
||||||
|
String lowerSnippet = snippet.toLowerCase(Locale.getDefault());
|
||||||
|
Spannable styledSnippet = new SpannableString(snippet);
|
||||||
|
StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
|
||||||
|
int queryLength = lowerQuery.length();
|
||||||
|
int offset = 0;
|
||||||
|
while (true) {
|
||||||
|
int pos = lowerSnippet.indexOf(lowerQuery, offset);
|
||||||
|
if (pos < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
styledSnippet.setSpan(boldSpan, pos, pos + queryLength, 0);
|
||||||
|
offset = pos + queryLength;
|
||||||
|
}
|
||||||
|
snippetView.setText(styledSnippet);
|
||||||
|
} else {
|
||||||
|
// This may be an error message, so don't try to bold the query terms within it
|
||||||
|
snippetView.setText(snippet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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.book;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The underlying data for a SBC result.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
final class SearchBookContentsResult {
|
||||||
|
|
||||||
|
private static String query = null;
|
||||||
|
|
||||||
|
private final String pageId;
|
||||||
|
private final String pageNumber;
|
||||||
|
private final String snippet;
|
||||||
|
private final boolean validSnippet;
|
||||||
|
|
||||||
|
SearchBookContentsResult(String pageId,
|
||||||
|
String pageNumber,
|
||||||
|
String snippet,
|
||||||
|
boolean validSnippet) {
|
||||||
|
this.pageId = pageId;
|
||||||
|
this.pageNumber = pageNumber;
|
||||||
|
this.snippet = snippet;
|
||||||
|
this.validSnippet = validSnippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setQuery(String query) {
|
||||||
|
SearchBookContentsResult.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPageId() {
|
||||||
|
return pageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPageNumber() {
|
||||||
|
return pageNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSnippet() {
|
||||||
|
return snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getValidSnippet() {
|
||||||
|
return validSnippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
|
||||||
|
import com.google.zxing.client.android.PreferencesActivity;
|
||||||
|
|
||||||
|
final class AutoFocusManager implements Camera.AutoFocusCallback {
|
||||||
|
|
||||||
|
private static final String TAG = AutoFocusManager.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final long AUTO_FOCUS_INTERVAL_MS = 2000L;
|
||||||
|
private static final Collection<String> FOCUS_MODES_CALLING_AF;
|
||||||
|
static {
|
||||||
|
FOCUS_MODES_CALLING_AF = new ArrayList<>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,251 @@
|
||||||
|
/*
|
||||||
|
* 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.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,332 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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!
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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.clipboard;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public final class ClipboardInterface {
|
||||||
|
|
||||||
|
private static final String TAG = ClipboardInterface.class.getSimpleName();
|
||||||
|
|
||||||
|
private ClipboardInterface() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CharSequence getText(Context context) {
|
||||||
|
ClipboardManager clipboard = getManager(context);
|
||||||
|
ClipData clip = clipboard.getPrimaryClip();
|
||||||
|
return hasText(context) ? clip.getItemAt(0).coerceToText(context) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setText(CharSequence text, Context context) {
|
||||||
|
if (text != null) {
|
||||||
|
try {
|
||||||
|
getManager(context).setPrimaryClip(ClipData.newPlainText(null, text));
|
||||||
|
} catch (NullPointerException | IllegalStateException e) {
|
||||||
|
// Have seen this in the wild, bizarrely
|
||||||
|
Log.w(TAG, "Clipboard bug", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasText(Context context) {
|
||||||
|
ClipboardManager clipboard = getManager(context);
|
||||||
|
ClipData clip = clipboard.getPrimaryClip();
|
||||||
|
return clip != null && clip.getItemCount() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClipboardManager getManager(Context context) {
|
||||||
|
return (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* 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<>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,243 @@
|
||||||
|
/*
|
||||||
|
* 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.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.zxing.WriterException;
|
||||||
|
import com.google.zxing.client.android.Contents;
|
||||||
|
import com.google.zxing.client.android.FinishListener;
|
||||||
|
import com.google.zxing.client.android.Intents;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class encodes data from an Intent into a QR code, and then displays it full screen so that
|
||||||
|
* another person can scan it with their device.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class EncodeActivity extends Activity {
|
||||||
|
|
||||||
|
private static final String TAG = EncodeActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final int MAX_BARCODE_FILENAME_LENGTH = 24;
|
||||||
|
private static final Pattern NOT_ALPHANUMERIC = Pattern.compile("[^A-Za-z0-9]");
|
||||||
|
private static final String USE_VCARD_KEY = "USE_VCARD";
|
||||||
|
|
||||||
|
private QRCodeEncoder qrCodeEncoder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle icicle) {
|
||||||
|
super.onCreate(icicle);
|
||||||
|
Intent intent = getIntent();
|
||||||
|
if (intent == null) {
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (Intents.Encode.ACTION.equals(action) || Intent.ACTION_SEND.equals(action)) {
|
||||||
|
setContentView(R.layout.encode);
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
MenuInflater menuInflater = getMenuInflater();
|
||||||
|
menuInflater.inflate(R.menu.encode, menu);
|
||||||
|
boolean useVcard = qrCodeEncoder != null && qrCodeEncoder.isUseVCard();
|
||||||
|
int encodeNameResource = useVcard ? R.string.menu_encode_mecard : R.string.menu_encode_vcard;
|
||||||
|
MenuItem encodeItem = menu.findItem(R.id.menu_encode);
|
||||||
|
encodeItem.setTitle(encodeNameResource);
|
||||||
|
Intent intent = getIntent();
|
||||||
|
if (intent != null) {
|
||||||
|
String type = intent.getStringExtra(Intents.Encode.TYPE);
|
||||||
|
encodeItem.setVisible(Contents.Type.CONTACT.equals(type));
|
||||||
|
}
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_share:
|
||||||
|
share();
|
||||||
|
return true;
|
||||||
|
case R.id.menu_encode:
|
||||||
|
Intent intent = getIntent();
|
||||||
|
if (intent == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
intent.putExtra(USE_VCARD_KEY, !qrCodeEncoder.isUseVCard());
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void share() {
|
||||||
|
QRCodeEncoder encoder = qrCodeEncoder;
|
||||||
|
if (encoder == null) { // Odd
|
||||||
|
Log.w(TAG, "No existing barcode to send?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String contents = encoder.getContents();
|
||||||
|
if (contents == null) {
|
||||||
|
Log.w(TAG, "No existing barcode to send?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap bitmap;
|
||||||
|
try {
|
||||||
|
bitmap = encoder.encodeAsBitmap();
|
||||||
|
} catch (WriterException we) {
|
||||||
|
Log.w(TAG, we);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bitmap == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File bsRoot = new File(Environment.getExternalStorageDirectory(), "BarcodeScanner");
|
||||||
|
File barcodesRoot = new File(bsRoot, "Barcodes");
|
||||||
|
if (!barcodesRoot.exists() && !barcodesRoot.mkdirs()) {
|
||||||
|
Log.w(TAG, "Couldn't make dir " + barcodesRoot);
|
||||||
|
showErrorMessage(R.string.msg_unmount_usb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File barcodeFile = new File(barcodesRoot, makeBarcodeFileName(contents) + ".png");
|
||||||
|
if (!barcodeFile.delete()) {
|
||||||
|
Log.w(TAG, "Could not delete " + barcodeFile);
|
||||||
|
// continue anyway
|
||||||
|
}
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
try {
|
||||||
|
fos = new FileOutputStream(barcodeFile);
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 0, fos);
|
||||||
|
} catch (FileNotFoundException fnfe) {
|
||||||
|
Log.w(TAG, "Couldn't access file " + barcodeFile + " due to " + fnfe);
|
||||||
|
showErrorMessage(R.string.msg_unmount_usb);
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
if (fos != null) {
|
||||||
|
try {
|
||||||
|
fos.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));
|
||||||
|
intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.app_name) + " - " + encoder.getTitle());
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, contents);
|
||||||
|
intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + barcodeFile.getAbsolutePath()));
|
||||||
|
intent.setType("image/png");
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
startActivity(Intent.createChooser(intent, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CharSequence makeBarcodeFileName(CharSequence contents) {
|
||||||
|
String fileName = NOT_ALPHANUMERIC.matcher(contents).replaceAll("_");
|
||||||
|
if (fileName.length() > MAX_BARCODE_FILENAME_LENGTH) {
|
||||||
|
fileName = fileName.substring(0, MAX_BARCODE_FILENAME_LENGTH);
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
// This assumes the view is full screen, which is a good assumption
|
||||||
|
WindowManager manager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||||
|
Display display = manager.getDefaultDisplay();
|
||||||
|
Point displaySize = new Point();
|
||||||
|
display.getSize(displaySize);
|
||||||
|
int width = displaySize.x;
|
||||||
|
int height = displaySize.y;
|
||||||
|
int smallerDimension = width < height ? width : height;
|
||||||
|
smallerDimension = smallerDimension * 7 / 8;
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
if (intent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
boolean useVCard = intent.getBooleanExtra(USE_VCARD_KEY, false);
|
||||||
|
qrCodeEncoder = new QRCodeEncoder(this, intent, smallerDimension, useVCard);
|
||||||
|
Bitmap bitmap = qrCodeEncoder.encodeAsBitmap();
|
||||||
|
if (bitmap == null) {
|
||||||
|
Log.w(TAG, "Could not encode barcode");
|
||||||
|
showErrorMessage(R.string.msg_encode_contents_failed);
|
||||||
|
qrCodeEncoder = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageView view = (ImageView) findViewById(R.id.image_view);
|
||||||
|
view.setImageBitmap(bitmap);
|
||||||
|
|
||||||
|
TextView contents = (TextView) findViewById(R.id.contents_text_view);
|
||||||
|
if (intent.getBooleanExtra(Intents.Encode.SHOW_CONTENTS, true)) {
|
||||||
|
contents.setText(qrCodeEncoder.getDisplayContents());
|
||||||
|
setTitle(qrCodeEncoder.getTitle());
|
||||||
|
} else {
|
||||||
|
contents.setText("");
|
||||||
|
setTitle("");
|
||||||
|
}
|
||||||
|
} catch (WriterException e) {
|
||||||
|
Log.w(TAG, "Could not encode barcode", e);
|
||||||
|
showErrorMessage(R.string.msg_encode_contents_failed);
|
||||||
|
qrCodeEncoder = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showErrorMessage(int message) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setMessage(message);
|
||||||
|
builder.setPositiveButton(R.string.button_ok, new FinishListener(this));
|
||||||
|
builder.setOnCancelListener(new FinishListener(this));
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* 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("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,390 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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 (Intents.Encode.ACTION.equals(action)) {
|
||||||
|
encodeContentsFromZXingIntent(intent);
|
||||||
|
} else if (Intent.ACTION_SEND.equals(action)) {
|
||||||
|
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) {
|
||||||
|
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<>(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.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* 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<>();
|
||||||
|
for (int i = 0; i < phones.size(); i++) {
|
||||||
|
if (phoneTypes.size() <= i) {
|
||||||
|
metadataForIndex.add(null);
|
||||||
|
} else {
|
||||||
|
Map<String,Set<String>> metadata = new HashMap<>();
|
||||||
|
metadataForIndex.add(metadata);
|
||||||
|
Set<String> typeTokens = new HashSet<>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* 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.history;
|
||||||
|
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class DBHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
private static final int DB_VERSION = 5;
|
||||||
|
private static final String DB_NAME = "barcode_scanner_history.db";
|
||||||
|
static final String TABLE_NAME = "history";
|
||||||
|
static final String ID_COL = "id";
|
||||||
|
static final String TEXT_COL = "text";
|
||||||
|
static final String FORMAT_COL = "format";
|
||||||
|
static final String DISPLAY_COL = "display";
|
||||||
|
static final String TIMESTAMP_COL = "timestamp";
|
||||||
|
static final String DETAILS_COL = "details";
|
||||||
|
|
||||||
|
DBHelper(Context context) {
|
||||||
|
super(context, DB_NAME, null, DB_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase sqLiteDatabase) {
|
||||||
|
sqLiteDatabase.execSQL(
|
||||||
|
"CREATE TABLE " + TABLE_NAME + " (" +
|
||||||
|
ID_COL + " INTEGER PRIMARY KEY, " +
|
||||||
|
TEXT_COL + " TEXT, " +
|
||||||
|
FORMAT_COL + " TEXT, " +
|
||||||
|
DISPLAY_COL + " TEXT, " +
|
||||||
|
TIMESTAMP_COL + " INTEGER, " +
|
||||||
|
DETAILS_COL + " TEXT);");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
|
||||||
|
sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
|
||||||
|
onCreate(sqLiteDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* Copyright 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.history;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ContextMenu;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import com.google.zxing.client.android.CaptureActivity;
|
||||||
|
import com.google.zxing.client.android.Intents;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
public final class HistoryActivity extends ListActivity {
|
||||||
|
|
||||||
|
private static final String TAG = HistoryActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
private HistoryManager historyManager;
|
||||||
|
private ArrayAdapter<HistoryItem> adapter;
|
||||||
|
private CharSequence originalTitle;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle icicle) {
|
||||||
|
super.onCreate(icicle);
|
||||||
|
this.historyManager = new HistoryManager(this);
|
||||||
|
adapter = new HistoryItemAdapter(this);
|
||||||
|
setListAdapter(adapter);
|
||||||
|
View listview = getListView();
|
||||||
|
registerForContextMenu(listview);
|
||||||
|
originalTitle = getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
reloadHistoryItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reloadHistoryItems() {
|
||||||
|
Iterable<HistoryItem> items = historyManager.buildHistoryItems();
|
||||||
|
adapter.clear();
|
||||||
|
for (HistoryItem item : items) {
|
||||||
|
adapter.add(item);
|
||||||
|
}
|
||||||
|
setTitle(originalTitle + " (" + adapter.getCount() + ')');
|
||||||
|
if (adapter.isEmpty()) {
|
||||||
|
adapter.add(new HistoryItem(null, null, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||||
|
if (adapter.getItem(position).getResult() != null) {
|
||||||
|
Intent intent = new Intent(this, CaptureActivity.class);
|
||||||
|
intent.putExtra(Intents.History.ITEM_NUMBER, position);
|
||||||
|
setResult(Activity.RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu,
|
||||||
|
View v,
|
||||||
|
ContextMenu.ContextMenuInfo menuInfo) {
|
||||||
|
int position = ((AdapterView.AdapterContextMenuInfo) menuInfo).position;
|
||||||
|
if (position >= adapter.getCount() || adapter.getItem(position).getResult() != null) {
|
||||||
|
menu.add(Menu.NONE, position, position, R.string.history_clear_one_history_text);
|
||||||
|
} // else it's just that dummy "Empty" message
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onContextItemSelected(MenuItem item) {
|
||||||
|
int position = item.getItemId();
|
||||||
|
historyManager.deleteHistoryItem(position);
|
||||||
|
reloadHistoryItems();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
if (historyManager.hasHistoryItems()) {
|
||||||
|
MenuInflater menuInflater = getMenuInflater();
|
||||||
|
menuInflater.inflate(R.menu.history, menu);
|
||||||
|
}
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_history_send:
|
||||||
|
CharSequence history = historyManager.buildHistory();
|
||||||
|
Parcelable historyFile = HistoryManager.saveHistory(history.toString());
|
||||||
|
if (historyFile == null) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setMessage(R.string.msg_unmount_usb);
|
||||||
|
builder.setPositiveButton(R.string.button_ok, null);
|
||||||
|
builder.show();
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
String subject = getResources().getString(R.string.history_email_title);
|
||||||
|
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, subject);
|
||||||
|
intent.putExtra(Intent.EXTRA_STREAM, historyFile);
|
||||||
|
intent.setType("text/csv");
|
||||||
|
try {
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (ActivityNotFoundException anfe) {
|
||||||
|
Log.w(TAG, anfe.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R.id.menu_history_clear_text:
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setMessage(R.string.msg_sure);
|
||||||
|
builder.setCancelable(true);
|
||||||
|
builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int i2) {
|
||||||
|
historyManager.clearHistory();
|
||||||
|
dialog.dismiss();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton(R.string.button_cancel, null);
|
||||||
|
builder.show();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 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.history;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
public final class HistoryItem {
|
||||||
|
|
||||||
|
private final Result result;
|
||||||
|
private final String display;
|
||||||
|
private final String details;
|
||||||
|
|
||||||
|
HistoryItem(Result result, String display, String details) {
|
||||||
|
this.result = result;
|
||||||
|
this.display = display;
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayAndDetails() {
|
||||||
|
StringBuilder displayResult = new StringBuilder();
|
||||||
|
if (display == null || display.isEmpty()) {
|
||||||
|
displayResult.append(result.getText());
|
||||||
|
} else {
|
||||||
|
displayResult.append(display);
|
||||||
|
}
|
||||||
|
if (details != null && !details.isEmpty()) {
|
||||||
|
displayResult.append(" : ").append(details);
|
||||||
|
}
|
||||||
|
return displayResult.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright 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.history;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
final class HistoryItemAdapter extends ArrayAdapter<HistoryItem> {
|
||||||
|
|
||||||
|
private final Context activity;
|
||||||
|
|
||||||
|
HistoryItemAdapter(Context activity) {
|
||||||
|
super(activity, R.layout.history_list_item, new ArrayList<HistoryItem>());
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View view, ViewGroup viewGroup) {
|
||||||
|
View layout;
|
||||||
|
if (view instanceof LinearLayout) {
|
||||||
|
layout = view;
|
||||||
|
} else {
|
||||||
|
LayoutInflater factory = LayoutInflater.from(activity);
|
||||||
|
layout = factory.inflate(R.layout.history_list_item, viewGroup, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryItem item = getItem(position);
|
||||||
|
Result result = item.getResult();
|
||||||
|
|
||||||
|
CharSequence title;
|
||||||
|
CharSequence detail;
|
||||||
|
if (result != null) {
|
||||||
|
title = result.getText();
|
||||||
|
detail = item.getDisplayAndDetails();
|
||||||
|
} else {
|
||||||
|
Resources resources = getContext().getResources();
|
||||||
|
title = resources.getString(R.string.history_empty);
|
||||||
|
detail = resources.getString(R.string.history_empty_detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
((TextView) layout.findViewById(R.id.history_title)).setText(title);
|
||||||
|
((TextView) layout.findViewById(R.id.history_detail)).setText(detail);
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,366 @@
|
||||||
|
/*
|
||||||
|
* 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.history;
|
||||||
|
|
||||||
|
import android.database.sqlite.SQLiteException;
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.client.android.Intents;
|
||||||
|
import com.google.zxing.client.android.PreferencesActivity;
|
||||||
|
import com.google.zxing.client.android.result.ResultHandler;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Manages functionality related to scan history.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class HistoryManager {
|
||||||
|
|
||||||
|
private static final String TAG = HistoryManager.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final int MAX_ITEMS = 2000;
|
||||||
|
|
||||||
|
private static final String[] COLUMNS = {
|
||||||
|
DBHelper.TEXT_COL,
|
||||||
|
DBHelper.DISPLAY_COL,
|
||||||
|
DBHelper.FORMAT_COL,
|
||||||
|
DBHelper.TIMESTAMP_COL,
|
||||||
|
DBHelper.DETAILS_COL,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] COUNT_COLUMN = { "COUNT(1)" };
|
||||||
|
|
||||||
|
private static final String[] ID_COL_PROJECTION = { DBHelper.ID_COL };
|
||||||
|
private static final String[] ID_DETAIL_COL_PROJECTION = { DBHelper.ID_COL, DBHelper.DETAILS_COL };
|
||||||
|
|
||||||
|
private final Activity activity;
|
||||||
|
private final boolean enableHistory;
|
||||||
|
|
||||||
|
public HistoryManager(Activity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
|
enableHistory = prefs.getBoolean(PreferencesActivity.KEY_ENABLE_HISTORY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasHistoryItems() {
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
db = helper.getReadableDatabase();
|
||||||
|
cursor = db.query(DBHelper.TABLE_NAME, COUNT_COLUMN, null, null, null, null, null);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
return cursor.getInt(0) > 0;
|
||||||
|
} finally {
|
||||||
|
close(cursor, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HistoryItem> buildHistoryItems() {
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
List<HistoryItem> items = new ArrayList<>();
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
db = helper.getReadableDatabase();
|
||||||
|
cursor = db.query(DBHelper.TABLE_NAME, COLUMNS, null, null, null, null, DBHelper.TIMESTAMP_COL + " DESC");
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
String text = cursor.getString(0);
|
||||||
|
String display = cursor.getString(1);
|
||||||
|
String format = cursor.getString(2);
|
||||||
|
long timestamp = cursor.getLong(3);
|
||||||
|
String details = cursor.getString(4);
|
||||||
|
Result result = new Result(text, null, null, BarcodeFormat.valueOf(format), timestamp);
|
||||||
|
items.add(new HistoryItem(result, display, details));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
close(cursor, db);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HistoryItem buildHistoryItem(int number) {
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
db = helper.getReadableDatabase();
|
||||||
|
cursor = db.query(DBHelper.TABLE_NAME, COLUMNS, null, null, null, null, DBHelper.TIMESTAMP_COL + " DESC");
|
||||||
|
cursor.move(number + 1);
|
||||||
|
String text = cursor.getString(0);
|
||||||
|
String display = cursor.getString(1);
|
||||||
|
String format = cursor.getString(2);
|
||||||
|
long timestamp = cursor.getLong(3);
|
||||||
|
String details = cursor.getString(4);
|
||||||
|
Result result = new Result(text, null, null, BarcodeFormat.valueOf(format), timestamp);
|
||||||
|
return new HistoryItem(result, display, details);
|
||||||
|
} finally {
|
||||||
|
close(cursor, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteHistoryItem(int number) {
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
db = helper.getWritableDatabase();
|
||||||
|
cursor = db.query(DBHelper.TABLE_NAME,
|
||||||
|
ID_COL_PROJECTION,
|
||||||
|
null, null, null, null,
|
||||||
|
DBHelper.TIMESTAMP_COL + " DESC");
|
||||||
|
cursor.move(number + 1);
|
||||||
|
db.delete(DBHelper.TABLE_NAME, DBHelper.ID_COL + '=' + cursor.getString(0), null);
|
||||||
|
} finally {
|
||||||
|
close(cursor, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHistoryItem(Result result, ResultHandler handler) {
|
||||||
|
// Do not save this item to the history if the preference is turned off, or the contents are
|
||||||
|
// considered secure.
|
||||||
|
if (!activity.getIntent().getBooleanExtra(Intents.Scan.SAVE_HISTORY, true) ||
|
||||||
|
handler.areContentsSecure() || !enableHistory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
|
if (!prefs.getBoolean(PreferencesActivity.KEY_REMEMBER_DUPLICATES, false)) {
|
||||||
|
deletePrevious(result.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(DBHelper.TEXT_COL, result.getText());
|
||||||
|
values.put(DBHelper.FORMAT_COL, result.getBarcodeFormat().toString());
|
||||||
|
values.put(DBHelper.DISPLAY_COL, handler.getDisplayContents().toString());
|
||||||
|
values.put(DBHelper.TIMESTAMP_COL, System.currentTimeMillis());
|
||||||
|
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
try {
|
||||||
|
db = helper.getWritableDatabase();
|
||||||
|
// Insert the new entry into the DB.
|
||||||
|
db.insert(DBHelper.TABLE_NAME, DBHelper.TIMESTAMP_COL, values);
|
||||||
|
} finally {
|
||||||
|
close(null, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHistoryItemDetails(String itemID, String itemDetails) {
|
||||||
|
// As we're going to do an update only we don't need need to worry
|
||||||
|
// about the preferences; if the item wasn't saved it won't be udpated
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
db = helper.getWritableDatabase();
|
||||||
|
cursor = db.query(DBHelper.TABLE_NAME,
|
||||||
|
ID_DETAIL_COL_PROJECTION,
|
||||||
|
DBHelper.TEXT_COL + "=?",
|
||||||
|
new String[] { itemID },
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
DBHelper.TIMESTAMP_COL + " DESC",
|
||||||
|
"1");
|
||||||
|
String oldID = null;
|
||||||
|
String oldDetails = null;
|
||||||
|
if (cursor.moveToNext()) {
|
||||||
|
oldID = cursor.getString(0);
|
||||||
|
oldDetails = cursor.getString(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldID != null) {
|
||||||
|
String newDetails;
|
||||||
|
if (oldDetails == null) {
|
||||||
|
newDetails = itemDetails;
|
||||||
|
} else if (oldDetails.contains(itemDetails)) {
|
||||||
|
newDetails = null;
|
||||||
|
} else {
|
||||||
|
newDetails = oldDetails + " : " + itemDetails;
|
||||||
|
}
|
||||||
|
if (newDetails != null) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(DBHelper.DETAILS_COL, newDetails);
|
||||||
|
db.update(DBHelper.TABLE_NAME, values, DBHelper.ID_COL + "=?", new String[] { oldID });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
close(cursor, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deletePrevious(String text) {
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
try {
|
||||||
|
db = helper.getWritableDatabase();
|
||||||
|
db.delete(DBHelper.TABLE_NAME, DBHelper.TEXT_COL + "=?", new String[] { text });
|
||||||
|
} finally {
|
||||||
|
close(null, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void trimHistory() {
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
db = helper.getWritableDatabase();
|
||||||
|
cursor = db.query(DBHelper.TABLE_NAME,
|
||||||
|
ID_COL_PROJECTION,
|
||||||
|
null, null, null, null,
|
||||||
|
DBHelper.TIMESTAMP_COL + " DESC");
|
||||||
|
cursor.move(MAX_ITEMS);
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
String id = cursor.getString(0);
|
||||||
|
Log.i(TAG, "Deleting scan history ID " + id);
|
||||||
|
db.delete(DBHelper.TABLE_NAME, DBHelper.ID_COL + '=' + id, null);
|
||||||
|
}
|
||||||
|
} catch (SQLiteException sqle) {
|
||||||
|
// We're seeing an error here when called in CaptureActivity.onCreate() in rare cases
|
||||||
|
// and don't understand it. First theory is that it's transient so can be safely ignored.
|
||||||
|
Log.w(TAG, sqle);
|
||||||
|
// continue
|
||||||
|
} finally {
|
||||||
|
close(cursor, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Builds a text representation of the scanning history. Each scan is encoded on one
|
||||||
|
* line, terminated by a line break (\r\n). The values in each line are comma-separated,
|
||||||
|
* and double-quoted. Double-quotes within values are escaped with a sequence of two
|
||||||
|
* double-quotes. The fields output are:</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Raw text</li>
|
||||||
|
* <li>Display text</li>
|
||||||
|
* <li>Format (e.g. QR_CODE)</li>
|
||||||
|
* <li>Unix timestamp (milliseconds since the epoch)</li>
|
||||||
|
* <li>Formatted version of timestamp</li>
|
||||||
|
* <li>Supplemental info (e.g. price info for a product barcode)</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
CharSequence buildHistory() {
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
db = helper.getWritableDatabase();
|
||||||
|
cursor = db.query(DBHelper.TABLE_NAME,
|
||||||
|
COLUMNS,
|
||||||
|
null, null, null, null,
|
||||||
|
DBHelper.TIMESTAMP_COL + " DESC");
|
||||||
|
|
||||||
|
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
|
||||||
|
StringBuilder historyText = new StringBuilder(1000);
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
|
||||||
|
historyText.append('"').append(massageHistoryField(cursor.getString(0))).append("\",");
|
||||||
|
historyText.append('"').append(massageHistoryField(cursor.getString(1))).append("\",");
|
||||||
|
historyText.append('"').append(massageHistoryField(cursor.getString(2))).append("\",");
|
||||||
|
historyText.append('"').append(massageHistoryField(cursor.getString(3))).append("\",");
|
||||||
|
|
||||||
|
// Add timestamp again, formatted
|
||||||
|
long timestamp = cursor.getLong(3);
|
||||||
|
historyText.append('"').append(massageHistoryField(
|
||||||
|
format.format(new Date(timestamp)))).append("\",");
|
||||||
|
|
||||||
|
// Above we're preserving the old ordering of columns which had formatted data in position 5
|
||||||
|
|
||||||
|
historyText.append('"').append(massageHistoryField(cursor.getString(4))).append("\"\r\n");
|
||||||
|
}
|
||||||
|
return historyText;
|
||||||
|
} finally {
|
||||||
|
close(cursor, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearHistory() {
|
||||||
|
SQLiteOpenHelper helper = new DBHelper(activity);
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
try {
|
||||||
|
db = helper.getWritableDatabase();
|
||||||
|
db.delete(DBHelper.TABLE_NAME, null, null);
|
||||||
|
} finally {
|
||||||
|
close(null, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uri saveHistory(String history) {
|
||||||
|
File bsRoot = new File(Environment.getExternalStorageDirectory(), "BarcodeScanner");
|
||||||
|
File historyRoot = new File(bsRoot, "History");
|
||||||
|
if (!historyRoot.exists() && !historyRoot.mkdirs()) {
|
||||||
|
Log.w(TAG, "Couldn't make dir " + historyRoot);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
File historyFile = new File(historyRoot, "history-" + System.currentTimeMillis() + ".csv");
|
||||||
|
OutputStreamWriter out = null;
|
||||||
|
try {
|
||||||
|
out = new OutputStreamWriter(new FileOutputStream(historyFile), Charset.forName("UTF-8"));
|
||||||
|
out.write(history);
|
||||||
|
return Uri.parse("file://" + historyFile.getAbsolutePath());
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.w(TAG, "Couldn't access file " + historyFile + " due to " + ioe);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (out != null) {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String massageHistoryField(String value) {
|
||||||
|
return value == null ? "" : value.replace("\"","\"\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void close(Cursor cursor, SQLiteDatabase database) {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
if (database != null) {
|
||||||
|
database.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
* 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.graphics.Typeface;
|
||||||
|
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 net.foucry.pilldroid.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(Typeface.BOLD), 0, namesLength, 0);
|
||||||
|
return styled;
|
||||||
|
} else {
|
||||||
|
return contents.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDisplayTitle() {
|
||||||
|
return R.string.result_address_book;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,516 @@
|
||||||
|
/*
|
||||||
|
* 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.Intents;
|
||||||
|
import com.google.zxing.client.android.LocaleManager;
|
||||||
|
import com.google.zxing.client.android.PreferencesActivity;
|
||||||
|
import com.google.zxing.client.android.book.SearchBookContentsActivity;
|
||||||
|
import com.google.zxing.client.result.ParsedResult;
|
||||||
|
import com.google.zxing.client.result.ParsedResultType;
|
||||||
|
import com.google.zxing.client.result.ResultParser;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
//import com.google.zxing.client.android.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* 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 com.google.zxing.client.result.ParsedResult;
|
||||||
|
import com.google.zxing.client.result.SMSParsedResult;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles SMS addresses, offering a choice of composing a new SMS or MMS message.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class SMSResultHandler extends ResultHandler {
|
||||||
|
private static final int[] buttons = {
|
||||||
|
R.string.button_sms,
|
||||||
|
R.string.button_mms
|
||||||
|
};
|
||||||
|
|
||||||
|
public SMSResultHandler(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) {
|
||||||
|
SMSParsedResult smsResult = (SMSParsedResult) getResult();
|
||||||
|
String number = smsResult.getNumbers()[0];
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
// Don't know of a way yet to express a SENDTO intent with multiple recipients
|
||||||
|
sendSMS(number, smsResult.getBody());
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
sendMMS(number, smsResult.getSubject(), smsResult.getBody());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getDisplayContents() {
|
||||||
|
SMSParsedResult smsResult = (SMSParsedResult) getResult();
|
||||||
|
String[] rawNumbers = smsResult.getNumbers();
|
||||||
|
String[] formattedNumbers = new String[rawNumbers.length];
|
||||||
|
for (int i = 0; i < rawNumbers.length; i++) {
|
||||||
|
formattedNumbers[i] = PhoneNumberUtils.formatNumber(rawNumbers[i]);
|
||||||
|
}
|
||||||
|
StringBuilder contents = new StringBuilder(50);
|
||||||
|
ParsedResult.maybeAppend(formattedNumbers, contents);
|
||||||
|
ParsedResult.maybeAppend(smsResult.getSubject(), contents);
|
||||||
|
ParsedResult.maybeAppend(smsResult.getBody(), contents);
|
||||||
|
return contents.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDisplayTitle() {
|
||||||
|
return R.string.result_sms;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.R;
|
||||||
|
import com.google.zxing.client.result.ParsedResult;
|
||||||
|
import com.google.zxing.client.result.TelParsedResult;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.telephony.PhoneNumberUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offers relevant actions for telephone numbers.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class TelResultHandler extends ResultHandler {
|
||||||
|
private static final int[] buttons = {
|
||||||
|
R.string.button_dial,
|
||||||
|
R.string.button_add_contact
|
||||||
|
};
|
||||||
|
|
||||||
|
public TelResultHandler(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) {
|
||||||
|
TelParsedResult telResult = (TelParsedResult) getResult();
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
dialPhoneFromUri(telResult.getTelURI());
|
||||||
|
// When dialer comes up, it allows underlying display activity to continue or something,
|
||||||
|
// but app can't get camera in this state. Avoid issues by just quitting, only in the
|
||||||
|
// case of a phone number
|
||||||
|
getActivity().finish();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
String[] numbers = new String[1];
|
||||||
|
numbers[0] = telResult.getNumber();
|
||||||
|
addPhoneOnlyContact(numbers, null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overriden so we can take advantage of Android's phone number hyphenation routines.
|
||||||
|
@Override
|
||||||
|
public CharSequence getDisplayContents() {
|
||||||
|
String contents = getResult().getDisplayResult();
|
||||||
|
contents = contents.replace("\r", "");
|
||||||
|
return PhoneNumberUtils.formatNumber(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDisplayTitle() {
|
||||||
|
return R.string.result_tel;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* 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 net.foucry.pilldroid.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* 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.android.LocaleManager;
|
||||||
|
import com.google.zxing.client.result.ParsedResult;
|
||||||
|
import com.google.zxing.client.result.URIParsedResult;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offers appropriate actions for URLS.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class URIResultHandler extends ResultHandler {
|
||||||
|
// URIs beginning with entries in this array will not be saved to history or copied to the
|
||||||
|
// clipboard for security.
|
||||||
|
private static final String[] SECURE_PROTOCOLS = {
|
||||||
|
"otpauth:"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int[] buttons = {
|
||||||
|
R.string.button_open_browser,
|
||||||
|
R.string.button_share_by_email,
|
||||||
|
R.string.button_share_by_sms,
|
||||||
|
R.string.button_search_book_contents,
|
||||||
|
};
|
||||||
|
|
||||||
|
public URIResultHandler(Activity activity, ParsedResult result) {
|
||||||
|
super(activity, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getButtonCount() {
|
||||||
|
if (LocaleManager.isBookSearchUrl(((URIParsedResult) getResult()).getURI())) {
|
||||||
|
return buttons.length;
|
||||||
|
}
|
||||||
|
return buttons.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getButtonText(int index) {
|
||||||
|
return buttons[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getDefaultButtonID() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleButtonPress(int index) {
|
||||||
|
URIParsedResult uriResult = (URIParsedResult) getResult();
|
||||||
|
String uri = uriResult.getURI();
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
openURL(uri);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
shareByEmail(uri);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
shareBySMS(uri);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
searchBookContents(uri);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDisplayTitle() {
|
||||||
|
return R.string.result_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsSecure() {
|
||||||
|
URIParsedResult uriResult = (URIParsedResult) getResult();
|
||||||
|
String uri = uriResult.getURI().toLowerCase(Locale.ENGLISH);
|
||||||
|
for (String secure : SECURE_PROTOCOLS) {
|
||||||
|
if (uri.startsWith(secure)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* 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.Context;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.zxing.client.android.CaptureActivity;
|
||||||
|
import com.google.zxing.client.android.wifi.WifiConfigManager;
|
||||||
|
import com.google.zxing.client.result.ParsedResult;
|
||||||
|
import com.google.zxing.client.result.WifiParsedResult;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles wifi access information.
|
||||||
|
*
|
||||||
|
* @author Vikram Aggarwal
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class WifiResultHandler extends ResultHandler {
|
||||||
|
|
||||||
|
private static final String TAG = WifiResultHandler.class.getSimpleName();
|
||||||
|
|
||||||
|
private final CaptureActivity parent;
|
||||||
|
|
||||||
|
public WifiResultHandler(CaptureActivity activity, ParsedResult result) {
|
||||||
|
super(activity, result);
|
||||||
|
parent = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getButtonCount() {
|
||||||
|
// We just need one button, and that is to configure the wireless. This could change in the future.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getButtonText(int index) {
|
||||||
|
return R.string.button_wifi;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleButtonPress(int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
WifiParsedResult wifiResult = (WifiParsedResult) getResult();
|
||||||
|
WifiManager wifiManager = (WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE);
|
||||||
|
if (wifiManager == null) {
|
||||||
|
Log.w(TAG, "No WifiManager available from device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Activity activity = getActivity();
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(activity.getApplicationContext(), R.string.wifi_changing_network, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
new WifiConfigManager(wifiManager).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, wifiResult);
|
||||||
|
parent.restartPreviewAfterDelay(0L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the name of the network and the network type to the user.
|
||||||
|
@Override
|
||||||
|
public CharSequence getDisplayContents() {
|
||||||
|
WifiParsedResult wifiResult = (WifiParsedResult) getResult();
|
||||||
|
return wifiResult.getSsid() + " (" + wifiResult.getNetworkEncryption() + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDisplayTitle() {
|
||||||
|
return R.string.result_wifi;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* 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.result.supplement;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.zxing.client.android.HttpHelper;
|
||||||
|
import com.google.zxing.client.android.LocaleManager;
|
||||||
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.json.JSONTokener;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kamil Kaczmarczyk
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class BookResultInfoRetriever extends SupplementalInfoRetriever {
|
||||||
|
|
||||||
|
private final String isbn;
|
||||||
|
private final String source;
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
BookResultInfoRetriever(TextView textView, String isbn, HistoryManager historyManager, Context context) {
|
||||||
|
super(textView, historyManager);
|
||||||
|
this.isbn = isbn;
|
||||||
|
this.source = context.getString(R.string.msg_google_books);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void retrieveSupplementalInfo() throws IOException {
|
||||||
|
|
||||||
|
CharSequence contents = HttpHelper.downloadViaHttp("https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn,
|
||||||
|
HttpHelper.ContentType.JSON);
|
||||||
|
|
||||||
|
if (contents.length() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String title;
|
||||||
|
String pages;
|
||||||
|
Collection<String> authors = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
JSONObject topLevel = (JSONObject) new JSONTokener(contents.toString()).nextValue();
|
||||||
|
JSONArray items = topLevel.optJSONArray("items");
|
||||||
|
if (items == null || items.isNull(0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject volumeInfo = ((JSONObject) items.get(0)).getJSONObject("volumeInfo");
|
||||||
|
if (volumeInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
title = volumeInfo.optString("title");
|
||||||
|
pages = volumeInfo.optString("pageCount");
|
||||||
|
|
||||||
|
JSONArray authorsArray = volumeInfo.optJSONArray("authors");
|
||||||
|
if (authorsArray != null && !authorsArray.isNull(0)) {
|
||||||
|
authors = new ArrayList<>(authorsArray.length());
|
||||||
|
for (int i = 0; i < authorsArray.length(); i++) {
|
||||||
|
authors.add(authorsArray.getString(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<String> newTexts = new ArrayList<>();
|
||||||
|
maybeAddText(title, newTexts);
|
||||||
|
maybeAddTextSeries(authors, newTexts);
|
||||||
|
maybeAddText(pages == null || pages.isEmpty() ? null : pages + "pp.", newTexts);
|
||||||
|
|
||||||
|
String baseBookUri = "http://www.google." + LocaleManager.getBookSearchCountryTLD(context)
|
||||||
|
+ "/search?tbm=bks&source=zxing&q=";
|
||||||
|
|
||||||
|
append(isbn, source, newTexts.toArray(new String[newTexts.size()]), baseBookUri + isbn);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* 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.result.supplement;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.zxing.client.android.HttpHelper;
|
||||||
|
import com.google.zxing.client.android.LocaleManager;
|
||||||
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Retrieves product information from Google Product search.</p>
|
||||||
|
*
|
||||||
|
* <p><strong>Please do not reuse this code.</strong> Using results in this way requires permission
|
||||||
|
* from Google, and that is not granted to users via this project.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class ProductResultInfoRetriever extends SupplementalInfoRetriever {
|
||||||
|
|
||||||
|
private static final Pattern[] PRODUCT_NAME_PRICE_PATTERNS = {
|
||||||
|
Pattern.compile(",event\\)\">([^<]+)</a></h3>.+<span class=psrp>([^<]+)</span>"),
|
||||||
|
Pattern.compile("owb63p\">([^<]+).+zdi3pb\">([^<]+)"),
|
||||||
|
};
|
||||||
|
|
||||||
|
private final String productID;
|
||||||
|
private final String source;
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
ProductResultInfoRetriever(TextView textView, String productID, HistoryManager historyManager, Context context) {
|
||||||
|
super(textView, historyManager);
|
||||||
|
this.productID = productID;
|
||||||
|
this.source = context.getString(R.string.msg_google_product);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void retrieveSupplementalInfo() throws IOException {
|
||||||
|
|
||||||
|
String encodedProductID = URLEncoder.encode(productID, "UTF-8");
|
||||||
|
String uri = "https://www.google." + LocaleManager.getProductSearchCountryTLD(context)
|
||||||
|
+ "/m/products?ie=utf8&oe=utf8&scoring=p&source=zxing&q=" + encodedProductID;
|
||||||
|
CharSequence content = HttpHelper.downloadViaHttp(uri, HttpHelper.ContentType.HTML);
|
||||||
|
|
||||||
|
for (Pattern p : PRODUCT_NAME_PRICE_PATTERNS) {
|
||||||
|
Matcher matcher = p.matcher(content);
|
||||||
|
if (matcher.find()) {
|
||||||
|
append(productID,
|
||||||
|
source,
|
||||||
|
new String[] { unescapeHTML(matcher.group(1)), unescapeHTML(matcher.group(2)) },
|
||||||
|
uri);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String unescapeHTML(String raw) {
|
||||||
|
return Html.fromHtml(raw).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* 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.result.supplement;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.text.method.LinkMovementMethod;
|
||||||
|
import android.text.style.URLSpan;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
|
||||||
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
|
import com.google.zxing.client.result.ISBNParsedResult;
|
||||||
|
import com.google.zxing.client.result.ParsedResult;
|
||||||
|
import com.google.zxing.client.result.ProductParsedResult;
|
||||||
|
import com.google.zxing.client.result.URIParsedResult;
|
||||||
|
|
||||||
|
public abstract class SupplementalInfoRetriever extends AsyncTask<Object,Object,Object> {
|
||||||
|
|
||||||
|
private static final String TAG = "SupplementalInfo";
|
||||||
|
|
||||||
|
public static void maybeInvokeRetrieval(TextView textView,
|
||||||
|
ParsedResult result,
|
||||||
|
HistoryManager historyManager,
|
||||||
|
Context context) {
|
||||||
|
try {
|
||||||
|
if (result instanceof URIParsedResult) {
|
||||||
|
SupplementalInfoRetriever uriRetriever =
|
||||||
|
new URIResultInfoRetriever(textView, (URIParsedResult) result, historyManager, context);
|
||||||
|
uriRetriever.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
SupplementalInfoRetriever titleRetriever =
|
||||||
|
new TitleRetriever(textView, (URIParsedResult) result, historyManager);
|
||||||
|
titleRetriever.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
} else if (result instanceof ProductParsedResult) {
|
||||||
|
ProductParsedResult productParsedResult = (ProductParsedResult) result;
|
||||||
|
String productID = productParsedResult.getProductID();
|
||||||
|
SupplementalInfoRetriever productRetriever =
|
||||||
|
new ProductResultInfoRetriever(textView, productID, historyManager, context);
|
||||||
|
productRetriever.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
} else if (result instanceof ISBNParsedResult) {
|
||||||
|
String isbn = ((ISBNParsedResult) result).getISBN();
|
||||||
|
SupplementalInfoRetriever productInfoRetriever =
|
||||||
|
new ProductResultInfoRetriever(textView, isbn, historyManager, context);
|
||||||
|
productInfoRetriever.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
SupplementalInfoRetriever bookInfoRetriever =
|
||||||
|
new BookResultInfoRetriever(textView, isbn, historyManager, context);
|
||||||
|
bookInfoRetriever.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
} catch (RejectedExecutionException ree) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final WeakReference<TextView> textViewRef;
|
||||||
|
private final WeakReference<HistoryManager> historyManagerRef;
|
||||||
|
private final Collection<Spannable> newContents;
|
||||||
|
private final Collection<String[]> newHistories;
|
||||||
|
|
||||||
|
SupplementalInfoRetriever(TextView textView, HistoryManager historyManager) {
|
||||||
|
textViewRef = new WeakReference<>(textView);
|
||||||
|
historyManagerRef = new WeakReference<>(historyManager);
|
||||||
|
newContents = new ArrayList<>();
|
||||||
|
newHistories = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Object doInBackground(Object... args) {
|
||||||
|
try {
|
||||||
|
retrieveSupplementalInfo();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final void onPostExecute(Object arg) {
|
||||||
|
TextView textView = textViewRef.get();
|
||||||
|
if (textView != null) {
|
||||||
|
for (CharSequence content : newContents) {
|
||||||
|
textView.append(content);
|
||||||
|
}
|
||||||
|
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
}
|
||||||
|
HistoryManager historyManager = historyManagerRef.get();
|
||||||
|
if (historyManager != null) {
|
||||||
|
for (String[] text : newHistories) {
|
||||||
|
historyManager.addHistoryItemDetails(text[0], text[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void retrieveSupplementalInfo() throws IOException;
|
||||||
|
|
||||||
|
final void append(String itemID, String source, String[] newTexts, String linkURL) {
|
||||||
|
|
||||||
|
StringBuilder newTextCombined = new StringBuilder();
|
||||||
|
|
||||||
|
if (source != null) {
|
||||||
|
newTextCombined.append(source).append(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
int linkStart = newTextCombined.length();
|
||||||
|
|
||||||
|
boolean first = true;
|
||||||
|
for (String newText : newTexts) {
|
||||||
|
if (first) {
|
||||||
|
newTextCombined.append(newText);
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
newTextCombined.append(" [");
|
||||||
|
newTextCombined.append(newText);
|
||||||
|
newTextCombined.append(']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int linkEnd = newTextCombined.length();
|
||||||
|
|
||||||
|
String newText = newTextCombined.toString();
|
||||||
|
Spannable content = new SpannableString(newText + "\n\n");
|
||||||
|
if (linkURL != null) {
|
||||||
|
// 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 (linkURL.startsWith("HTTP://")) {
|
||||||
|
linkURL = "http" + linkURL.substring(4);
|
||||||
|
} else if (linkURL.startsWith("HTTPS://")) {
|
||||||
|
linkURL = "https" + linkURL.substring(5);
|
||||||
|
}
|
||||||
|
content.setSpan(new URLSpan(linkURL), linkStart, linkEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
newContents.add(content);
|
||||||
|
newHistories.add(new String[] {itemID, newText});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void maybeAddText(String text, Collection<String> texts) {
|
||||||
|
if (text != null && !text.isEmpty()) {
|
||||||
|
texts.add(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void maybeAddTextSeries(Collection<String> textSeries, Collection<String> texts) {
|
||||||
|
if (textSeries != null && !textSeries.isEmpty()) {
|
||||||
|
boolean first = true;
|
||||||
|
StringBuilder authorsText = new StringBuilder();
|
||||||
|
for (String author : textSeries) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
authorsText.append(", ");
|
||||||
|
}
|
||||||
|
authorsText.append(author);
|
||||||
|
}
|
||||||
|
texts.add(authorsText.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright 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.result.supplement;
|
||||||
|
|
||||||
|
import android.text.Html;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import com.google.zxing.client.android.HttpHelper;
|
||||||
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
|
import com.google.zxing.client.result.URIParsedResult;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the title of a web page as supplemental info.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class TitleRetriever extends SupplementalInfoRetriever {
|
||||||
|
|
||||||
|
private static final Pattern TITLE_PATTERN = Pattern.compile("<title>([^<]+)");
|
||||||
|
private static final int MAX_TITLE_LEN = 100;
|
||||||
|
|
||||||
|
private final String httpUrl;
|
||||||
|
|
||||||
|
TitleRetriever(TextView textView, URIParsedResult result, HistoryManager historyManager) {
|
||||||
|
super(textView, historyManager);
|
||||||
|
this.httpUrl = result.getURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void retrieveSupplementalInfo() {
|
||||||
|
CharSequence contents;
|
||||||
|
try {
|
||||||
|
contents = HttpHelper.downloadViaHttp(httpUrl, HttpHelper.ContentType.HTML, 4096);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
// ignore this
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (contents != null && contents.length() > 0) {
|
||||||
|
Matcher m = TITLE_PATTERN.matcher(contents);
|
||||||
|
if (m.find()) {
|
||||||
|
String title = m.group(1);
|
||||||
|
if (title != null && !title.isEmpty()) {
|
||||||
|
title = Html.fromHtml(title).toString();
|
||||||
|
if (title.length() > MAX_TITLE_LEN) {
|
||||||
|
title = title.substring(0, MAX_TITLE_LEN) + "...";
|
||||||
|
}
|
||||||
|
append(httpUrl, null, new String[] {title}, httpUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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.result.supplement;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.zxing.client.android.HttpHelper;
|
||||||
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
|
import com.google.zxing.client.result.URIParsedResult;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
final class URIResultInfoRetriever extends SupplementalInfoRetriever {
|
||||||
|
|
||||||
|
private static final int MAX_REDIRECTS = 5;
|
||||||
|
|
||||||
|
private final URIParsedResult result;
|
||||||
|
private final String redirectString;
|
||||||
|
|
||||||
|
URIResultInfoRetriever(TextView textView, URIParsedResult result, HistoryManager historyManager, Context context) {
|
||||||
|
super(textView, historyManager);
|
||||||
|
redirectString = context.getString(R.string.msg_redirect);
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void retrieveSupplementalInfo() throws IOException {
|
||||||
|
URI oldURI;
|
||||||
|
try {
|
||||||
|
oldURI = new URI(result.getURI());
|
||||||
|
} catch (URISyntaxException ignored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
URI newURI = HttpHelper.unredirect(oldURI);
|
||||||
|
int count = 0;
|
||||||
|
while (count++ < MAX_REDIRECTS && !oldURI.equals(newURI)) {
|
||||||
|
append(result.getDisplayResult(),
|
||||||
|
null,
|
||||||
|
new String[] { redirectString + " : " + newURI },
|
||||||
|
newURI.toString());
|
||||||
|
oldURI = newURI;
|
||||||
|
newURI = HttpHelper.unredirect(newURI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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.share;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
final class AppInfo implements Comparable<AppInfo> {
|
||||||
|
|
||||||
|
private final String packageName;
|
||||||
|
private final String label;
|
||||||
|
private final Drawable icon;
|
||||||
|
|
||||||
|
AppInfo(String packageName, String label, Drawable icon) {
|
||||||
|
this.packageName = packageName;
|
||||||
|
this.label = label;
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPackageName() {
|
||||||
|
return packageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
Drawable getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(AppInfo another) {
|
||||||
|
return label.compareTo(another.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return label.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (!(other instanceof AppInfo)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AppInfo another = (AppInfo) other;
|
||||||
|
return label.equals(another.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* 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.share;
|
||||||
|
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Adapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class AppPickerActivity extends ListActivity {
|
||||||
|
|
||||||
|
private AsyncTask<Object,Object,List<AppInfo>> backgroundTask;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
backgroundTask = new LoadPackagesAsyncTask(this);
|
||||||
|
backgroundTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
AsyncTask<?,?,?> task = backgroundTask;
|
||||||
|
if (task != null) {
|
||||||
|
task.cancel(true);
|
||||||
|
backgroundTask = null;
|
||||||
|
}
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onListItemClick(ListView l, View view, int position, long id) {
|
||||||
|
Adapter adapter = getListAdapter();
|
||||||
|
if (position >= 0 && position < adapter.getCount()) {
|
||||||
|
String packageName = ((AppInfo) adapter.getItem(position)).getPackageName();
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
intent.putExtra("url", "market://details?id=" + packageName); // Browser.BookmarkColumns.URL
|
||||||
|
setResult(RESULT_OK, intent);
|
||||||
|
} else {
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* 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.share;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom adapter designed to fetch bookmarks from a cursor. Before Honeycomb we used
|
||||||
|
* SimpleCursorAdapter, but it assumes the existence of an _id column, and the bookmark schema was
|
||||||
|
* rewritten for HC without one. This caused the app to crash, hence this new class, which is
|
||||||
|
* forwards and backwards compatible.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
final class BookmarkAdapter extends BaseAdapter {
|
||||||
|
private final Context context;
|
||||||
|
private final Cursor cursor;
|
||||||
|
|
||||||
|
BookmarkAdapter(Context context, Cursor cursor) {
|
||||||
|
this.context = context;
|
||||||
|
this.cursor = cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return cursor.isClosed() ? 0 : cursor.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int index) {
|
||||||
|
// Not used, so no point in retrieving it.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int index) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int index, View view, ViewGroup viewGroup) {
|
||||||
|
View layout;
|
||||||
|
if (view instanceof LinearLayout) {
|
||||||
|
layout = view;
|
||||||
|
} else {
|
||||||
|
LayoutInflater factory = LayoutInflater.from(context);
|
||||||
|
layout = factory.inflate(R.layout.bookmark_picker_list_item, viewGroup, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cursor.isClosed()) {
|
||||||
|
cursor.moveToPosition(index);
|
||||||
|
CharSequence title = cursor.getString(BookmarkPickerActivity.TITLE_COLUMN);
|
||||||
|
((TextView) layout.findViewById(R.id.bookmark_title)).setText(title);
|
||||||
|
CharSequence url = cursor.getString(BookmarkPickerActivity.URL_COLUMN);
|
||||||
|
((TextView) layout.findViewById(R.id.bookmark_url)).setText(url);
|
||||||
|
} // Otherwise... just don't update as the object is shutting down
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* 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.share;
|
||||||
|
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is only needed because I can't successfully send an ACTION_PICK intent to
|
||||||
|
* com.android.browser.BrowserBookmarksPage. It can go away if that starts working in the future.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class BookmarkPickerActivity extends ListActivity {
|
||||||
|
|
||||||
|
private static final String TAG = BookmarkPickerActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final String[] BOOKMARK_PROJECTION = {
|
||||||
|
"title", // Browser.BookmarkColumns.TITLE
|
||||||
|
"url", // Browser.BookmarkColumns.URL
|
||||||
|
};
|
||||||
|
// Copied from android.provider.Browser.BOOKMARKS_URI:
|
||||||
|
private static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
|
||||||
|
|
||||||
|
static final int TITLE_COLUMN = 0;
|
||||||
|
static final int URL_COLUMN = 1;
|
||||||
|
|
||||||
|
private static final String BOOKMARK_SELECTION = "bookmark = 1 AND url IS NOT NULL";
|
||||||
|
|
||||||
|
private Cursor cursor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
cursor = getContentResolver().query(BOOKMARKS_URI, BOOKMARK_PROJECTION,
|
||||||
|
BOOKMARK_SELECTION, null, null);
|
||||||
|
if (cursor == null) {
|
||||||
|
Log.w(TAG, "No cursor returned for bookmark query");
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setListAdapter(new BookmarkAdapter(this, cursor));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
cursor = null;
|
||||||
|
}
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onListItemClick(ListView l, View view, int position, long id) {
|
||||||
|
if (!cursor.isClosed() && cursor.moveToPosition(position)) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
intent.putExtra("title", cursor.getString(TITLE_COLUMN)); // Browser.BookmarkColumns.TITLE
|
||||||
|
intent.putExtra("url", cursor.getString(URL_COLUMN)); // Browser.BookmarkColumns.URL
|
||||||
|
setResult(RESULT_OK, intent);
|
||||||
|
} else {
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* 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.share;
|
||||||
|
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageItemInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ListAdapter;
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a list of packages installed on the device asynchronously.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class LoadPackagesAsyncTask extends AsyncTask<Object,Object,List<AppInfo>> {
|
||||||
|
|
||||||
|
private static final String[] PKG_PREFIX_WHITELIST = {
|
||||||
|
"com.google.android.apps.",
|
||||||
|
};
|
||||||
|
private static final String[] PKG_PREFIX_BLACKLIST = {
|
||||||
|
"com.android.",
|
||||||
|
"android",
|
||||||
|
"com.google.android.",
|
||||||
|
"com.htc",
|
||||||
|
};
|
||||||
|
|
||||||
|
private final ListActivity activity;
|
||||||
|
|
||||||
|
LoadPackagesAsyncTask(ListActivity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<AppInfo> doInBackground(Object... objects) {
|
||||||
|
List<AppInfo> labelsPackages = new ArrayList<>();
|
||||||
|
PackageManager packageManager = activity.getPackageManager();
|
||||||
|
Iterable<ApplicationInfo> appInfos = packageManager.getInstalledApplications(0);
|
||||||
|
for (PackageItemInfo appInfo : appInfos) {
|
||||||
|
String packageName = appInfo.packageName;
|
||||||
|
if (!isHidden(packageName)) {
|
||||||
|
CharSequence label = appInfo.loadLabel(packageManager);
|
||||||
|
Drawable icon = appInfo.loadIcon(packageManager);
|
||||||
|
if (label != null) {
|
||||||
|
labelsPackages.add(new AppInfo(packageName, label.toString(), icon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(labelsPackages);
|
||||||
|
return labelsPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isHidden(String packageName) {
|
||||||
|
if (packageName == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (String prefix : PKG_PREFIX_WHITELIST) {
|
||||||
|
if (packageName.startsWith(prefix)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String prefix : PKG_PREFIX_BLACKLIST) {
|
||||||
|
if (packageName.startsWith(prefix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(final List<AppInfo> results) {
|
||||||
|
ListAdapter listAdapter = new ArrayAdapter<AppInfo>(activity,
|
||||||
|
R.layout.app_picker_list_item,
|
||||||
|
R.id.app_picker_list_item_label,
|
||||||
|
results) {
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
View view = super.getView(position, convertView, parent);
|
||||||
|
Drawable icon = results.get(position).getIcon();
|
||||||
|
if (icon != null) {
|
||||||
|
((ImageView) view.findViewById(R.id.app_picker_list_item_icon)).setImageDrawable(icon);
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
activity.setListAdapter(listAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,306 @@
|
||||||
|
/*
|
||||||
|
* 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.share;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.client.android.Contents;
|
||||||
|
import com.google.zxing.client.android.Intents;
|
||||||
|
import com.google.zxing.client.android.clipboard.ClipboardInterface;
|
||||||
|
|
||||||
|
import net.foucry.pilldroid.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Barcode Scanner can share data like contacts and bookmarks by displaying a QR Code on screen,
|
||||||
|
* such that another user can scan the barcode with their phone.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class ShareActivity extends Activity {
|
||||||
|
|
||||||
|
private static final String TAG = ShareActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final int PICK_BOOKMARK = 0;
|
||||||
|
private static final int PICK_CONTACT = 1;
|
||||||
|
private static final int PICK_APP = 2;
|
||||||
|
|
||||||
|
private View clipboardButton;
|
||||||
|
|
||||||
|
private final View.OnClickListener contactListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
startActivityForResult(intent, PICK_CONTACT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final View.OnClickListener bookmarkListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_PICK);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
intent.setClassName(ShareActivity.this, BookmarkPickerActivity.class.getName());
|
||||||
|
startActivityForResult(intent, PICK_BOOKMARK);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final View.OnClickListener appListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_PICK);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
intent.setClassName(ShareActivity.this, AppPickerActivity.class.getName());
|
||||||
|
startActivityForResult(intent, PICK_APP);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final View.OnClickListener clipboardListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
// Should always be true, because we grey out the clipboard button in onResume() if it's empty
|
||||||
|
CharSequence text = ClipboardInterface.getText(ShareActivity.this);
|
||||||
|
if (text != null) {
|
||||||
|
launchSearch(text.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final View.OnKeyListener textListener = new View.OnKeyListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View view, int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||||
|
String text = ((TextView) view).getText().toString();
|
||||||
|
if (text != null && !text.isEmpty()) {
|
||||||
|
launchSearch(text);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void launchSearch(String text) {
|
||||||
|
Intent intent = new Intent(Intents.Encode.ACTION);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
intent.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT);
|
||||||
|
intent.putExtra(Intents.Encode.DATA, text);
|
||||||
|
intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle icicle) {
|
||||||
|
super.onCreate(icicle);
|
||||||
|
setContentView(R.layout.share);
|
||||||
|
|
||||||
|
findViewById(R.id.share_contact_button).setOnClickListener(contactListener);
|
||||||
|
if (Build.VERSION.SDK_INT >= 23) { // Marshmallow / 6.0
|
||||||
|
// Can't access bookmarks in 6.0+
|
||||||
|
findViewById(R.id.share_bookmark_button).setEnabled(false);
|
||||||
|
} else {
|
||||||
|
findViewById(R.id.share_bookmark_button).setOnClickListener(bookmarkListener);
|
||||||
|
}
|
||||||
|
findViewById(R.id.share_app_button).setOnClickListener(appListener);
|
||||||
|
clipboardButton = findViewById(R.id.share_clipboard_button);
|
||||||
|
clipboardButton.setOnClickListener(clipboardListener);
|
||||||
|
findViewById(R.id.share_text_view).setOnKeyListener(textListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
clipboardButton.setEnabled(ClipboardInterface.hasText(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
switch (requestCode) {
|
||||||
|
case PICK_BOOKMARK:
|
||||||
|
case PICK_APP:
|
||||||
|
showTextAsBarcode(intent.getStringExtra("url")); // Browser.BookmarkColumns.URL
|
||||||
|
break;
|
||||||
|
case PICK_CONTACT:
|
||||||
|
// Data field is content://contacts/people/984
|
||||||
|
showContactAsBarcode(intent.getData());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showTextAsBarcode(String text) {
|
||||||
|
Log.i(TAG, "Showing text as barcode: " + text);
|
||||||
|
if (text == null) {
|
||||||
|
return; // Show error?
|
||||||
|
}
|
||||||
|
Intent intent = new Intent(Intents.Encode.ACTION);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
intent.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT);
|
||||||
|
intent.putExtra(Intents.Encode.DATA, text);
|
||||||
|
intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a contact Uri and does the necessary database lookups to retrieve that person's info,
|
||||||
|
* then sends an Encode intent to render it as a QR Code.
|
||||||
|
*
|
||||||
|
* @param contactUri A Uri of the form content://contacts/people/17
|
||||||
|
*/
|
||||||
|
private void showContactAsBarcode(Uri contactUri) {
|
||||||
|
Log.i(TAG, "Showing contact URI as barcode: " + contactUri);
|
||||||
|
if (contactUri == null) {
|
||||||
|
return; // Show error?
|
||||||
|
}
|
||||||
|
ContentResolver resolver = getContentResolver();
|
||||||
|
|
||||||
|
Cursor cursor;
|
||||||
|
try {
|
||||||
|
// We're seeing about six reports a week of this exception although I don't understand why.
|
||||||
|
cursor = resolver.query(contactUri, null, null, null, null);
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cursor == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String id;
|
||||||
|
String name;
|
||||||
|
boolean hasPhone;
|
||||||
|
try {
|
||||||
|
if (!cursor.moveToFirst()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = cursor.getString(cursor.getColumnIndex(BaseColumns._ID));
|
||||||
|
name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
|
||||||
|
hasPhone = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0;
|
||||||
|
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't require a name to be present, this contact might be just a phone number.
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
if (name != null && !name.isEmpty()) {
|
||||||
|
bundle.putString(ContactsContract.Intents.Insert.NAME, massageContactData(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPhone) {
|
||||||
|
Cursor phonesCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
|
||||||
|
null,
|
||||||
|
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + '=' + id,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
if (phonesCursor != null) {
|
||||||
|
try {
|
||||||
|
int foundPhone = 0;
|
||||||
|
int phonesNumberColumn = phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||||
|
int phoneTypeColumn = phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
|
||||||
|
while (phonesCursor.moveToNext() && foundPhone < Contents.PHONE_KEYS.length) {
|
||||||
|
String number = phonesCursor.getString(phonesNumberColumn);
|
||||||
|
if (number != null && !number.isEmpty()) {
|
||||||
|
bundle.putString(Contents.PHONE_KEYS[foundPhone], massageContactData(number));
|
||||||
|
}
|
||||||
|
int type = phonesCursor.getInt(phoneTypeColumn);
|
||||||
|
bundle.putInt(Contents.PHONE_TYPE_KEYS[foundPhone], type);
|
||||||
|
foundPhone++;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
phonesCursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor methodsCursor = resolver.query(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI,
|
||||||
|
null,
|
||||||
|
ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID + '=' + id,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
if (methodsCursor != null) {
|
||||||
|
try {
|
||||||
|
if (methodsCursor.moveToNext()) {
|
||||||
|
String data = methodsCursor.getString(
|
||||||
|
methodsCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS));
|
||||||
|
if (data != null && !data.isEmpty()) {
|
||||||
|
bundle.putString(ContactsContract.Intents.Insert.POSTAL, massageContactData(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
methodsCursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor emailCursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
|
||||||
|
null,
|
||||||
|
ContactsContract.CommonDataKinds.Email.CONTACT_ID + '=' + id,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
if (emailCursor != null) {
|
||||||
|
try {
|
||||||
|
int foundEmail = 0;
|
||||||
|
int emailColumn = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
|
||||||
|
while (emailCursor.moveToNext() && foundEmail < Contents.EMAIL_KEYS.length) {
|
||||||
|
String email = emailCursor.getString(emailColumn);
|
||||||
|
if (email != null && !email.isEmpty()) {
|
||||||
|
bundle.putString(Contents.EMAIL_KEYS[foundEmail], massageContactData(email));
|
||||||
|
}
|
||||||
|
foundEmail++;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
emailCursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = new Intent(Intents.Encode.ACTION);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
|
intent.putExtra(Intents.Encode.TYPE, Contents.Type.CONTACT);
|
||||||
|
intent.putExtra(Intents.Encode.DATA, bundle);
|
||||||
|
intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
|
||||||
|
|
||||||
|
Log.i(TAG, "Sending bundle for encoding: " + bundle);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String massageContactData(String data) {
|
||||||
|
// For now -- make sure we don't put newlines in shared contact data. It messes up
|
||||||
|
// any known encoding of contact data. Replace with space.
|
||||||
|
if (data.indexOf('\n') >= 0) {
|
||||||
|
data = data.replace("\n", " ");
|
||||||
|
}
|
||||||
|
if (data.indexOf('\r') >= 0) {
|
||||||
|
data = data.replace("\r", " ");
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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.wifi;
|
||||||
|
|
||||||
|
enum NetworkType {
|
||||||
|
|
||||||
|
WEP,
|
||||||
|
WPA,
|
||||||
|
NO_PASSWORD;
|
||||||
|
|
||||||
|
static NetworkType forIntentValue(String networkTypeString) {
|
||||||
|
if (networkTypeString == null) {
|
||||||
|
return NO_PASSWORD;
|
||||||
|
}
|
||||||
|
if ("WPA".equals(networkTypeString)) {
|
||||||
|
return WPA;
|
||||||
|
}
|
||||||
|
if ("WEP".equals(networkTypeString)) {
|
||||||
|
return WEP;
|
||||||
|
}
|
||||||
|
if ("nopass".equals(networkTypeString)) {
|
||||||
|
return NO_PASSWORD;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(networkTypeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,226 @@
|
||||||
|
/*
|
||||||
|
* 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.wifi;
|
||||||
|
|
||||||
|
import android.net.wifi.WifiConfiguration;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.google.zxing.client.result.WifiParsedResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vikram Aggarwal
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class WifiConfigManager extends AsyncTask<WifiParsedResult,Object,Object> {
|
||||||
|
|
||||||
|
private static final String TAG = WifiConfigManager.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final Pattern HEX_DIGITS = Pattern.compile("[0-9A-Fa-f]+");
|
||||||
|
|
||||||
|
private final WifiManager wifiManager;
|
||||||
|
|
||||||
|
public WifiConfigManager(WifiManager wifiManager) {
|
||||||
|
this.wifiManager = wifiManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object doInBackground(WifiParsedResult... args) {
|
||||||
|
WifiParsedResult theWifiResult = args[0];
|
||||||
|
// Start WiFi, otherwise nothing will work
|
||||||
|
if (!wifiManager.isWifiEnabled()) {
|
||||||
|
Log.i(TAG, "Enabling wi-fi...");
|
||||||
|
if (wifiManager.setWifiEnabled(true)) {
|
||||||
|
Log.i(TAG, "Wi-fi enabled");
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Wi-fi could not be enabled!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// This happens very quickly, but need to wait for it to enable. A little busy wait?
|
||||||
|
int count = 0;
|
||||||
|
while (!wifiManager.isWifiEnabled()) {
|
||||||
|
if (count >= 10) {
|
||||||
|
Log.i(TAG, "Took too long to enable wi-fi, quitting");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Still waiting for wi-fi to enable...");
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000L);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String networkTypeString = theWifiResult.getNetworkEncryption();
|
||||||
|
NetworkType networkType;
|
||||||
|
try {
|
||||||
|
networkType = NetworkType.forIntentValue(networkTypeString);
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
Log.w(TAG, "Bad network type; see NetworkType values: " + networkTypeString);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (networkType == NetworkType.NO_PASSWORD) {
|
||||||
|
changeNetworkUnEncrypted(wifiManager, theWifiResult);
|
||||||
|
} else {
|
||||||
|
String password = theWifiResult.getPassword();
|
||||||
|
if (password != null && !password.isEmpty()) {
|
||||||
|
if (networkType == NetworkType.WEP) {
|
||||||
|
changeNetworkWEP(wifiManager, theWifiResult);
|
||||||
|
} else if (networkType == NetworkType.WPA) {
|
||||||
|
changeNetworkWPA(wifiManager, theWifiResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the network: either create a new network or modify an existing network
|
||||||
|
* @param config the new network configuration
|
||||||
|
*/
|
||||||
|
private static void updateNetwork(WifiManager wifiManager, WifiConfiguration config) {
|
||||||
|
Integer foundNetworkID = findNetworkInExistingConfig(wifiManager, config.SSID);
|
||||||
|
if (foundNetworkID != null) {
|
||||||
|
Log.i(TAG, "Removing old configuration for network " + config.SSID);
|
||||||
|
wifiManager.removeNetwork(foundNetworkID);
|
||||||
|
wifiManager.saveConfiguration();
|
||||||
|
}
|
||||||
|
int networkId = wifiManager.addNetwork(config);
|
||||||
|
if (networkId >= 0) {
|
||||||
|
// Try to disable the current network and start a new one.
|
||||||
|
if (wifiManager.enableNetwork(networkId, true)) {
|
||||||
|
Log.i(TAG, "Associating to network " + config.SSID);
|
||||||
|
wifiManager.saveConfiguration();
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Failed to enable network " + config.SSID);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Unable to add network " + config.SSID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WifiConfiguration changeNetworkCommon(WifiParsedResult wifiResult) {
|
||||||
|
WifiConfiguration config = new WifiConfiguration();
|
||||||
|
config.allowedAuthAlgorithms.clear();
|
||||||
|
config.allowedGroupCiphers.clear();
|
||||||
|
config.allowedKeyManagement.clear();
|
||||||
|
config.allowedPairwiseCiphers.clear();
|
||||||
|
config.allowedProtocols.clear();
|
||||||
|
// Android API insists that an ascii SSID must be quoted to be correctly handled.
|
||||||
|
config.SSID = quoteNonHex(wifiResult.getSsid());
|
||||||
|
config.hiddenSSID = wifiResult.isHidden();
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding a WEP network
|
||||||
|
private static void changeNetworkWEP(WifiManager wifiManager, WifiParsedResult wifiResult) {
|
||||||
|
WifiConfiguration config = changeNetworkCommon(wifiResult);
|
||||||
|
config.wepKeys[0] = quoteNonHex(wifiResult.getPassword(), 10, 26, 58);
|
||||||
|
config.wepTxKeyIndex = 0;
|
||||||
|
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
|
||||||
|
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
|
||||||
|
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
|
||||||
|
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
|
||||||
|
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
|
||||||
|
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
|
||||||
|
updateNetwork(wifiManager, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding a WPA or WPA2 network
|
||||||
|
private static void changeNetworkWPA(WifiManager wifiManager, WifiParsedResult wifiResult) {
|
||||||
|
WifiConfiguration config = changeNetworkCommon(wifiResult);
|
||||||
|
// Hex passwords that are 64 bits long are not to be quoted.
|
||||||
|
config.preSharedKey = quoteNonHex(wifiResult.getPassword(), 64);
|
||||||
|
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
|
||||||
|
config.allowedProtocols.set(WifiConfiguration.Protocol.WPA); // For WPA
|
||||||
|
config.allowedProtocols.set(WifiConfiguration.Protocol.RSN); // For WPA2
|
||||||
|
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
|
||||||
|
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
|
||||||
|
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
|
||||||
|
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
|
||||||
|
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
|
||||||
|
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
|
||||||
|
updateNetwork(wifiManager, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding an open, unsecured network
|
||||||
|
private static void changeNetworkUnEncrypted(WifiManager wifiManager, WifiParsedResult wifiResult) {
|
||||||
|
WifiConfiguration config = changeNetworkCommon(wifiResult);
|
||||||
|
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
|
||||||
|
updateNetwork(wifiManager, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer findNetworkInExistingConfig(WifiManager wifiManager, String ssid) {
|
||||||
|
Iterable<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks();
|
||||||
|
if (existingConfigs != null) {
|
||||||
|
for (WifiConfiguration existingConfig : existingConfigs) {
|
||||||
|
String existingSSID = existingConfig.SSID;
|
||||||
|
if (existingSSID != null && existingSSID.equals(ssid)) {
|
||||||
|
return existingConfig.networkId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String quoteNonHex(String value, int... allowedLengths) {
|
||||||
|
return isHexOfLength(value, allowedLengths) ? value : convertToQuotedString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encloses the incoming string inside double quotes, if it isn't already quoted.
|
||||||
|
* @param s the input string
|
||||||
|
* @return a quoted string, of the form "input". If the input string is null, it returns null
|
||||||
|
* as well.
|
||||||
|
*/
|
||||||
|
private static String convertToQuotedString(String s) {
|
||||||
|
if (s == null || s.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// If already quoted, return as-is
|
||||||
|
if (s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"') {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return '\"' + s + '\"';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value input to check
|
||||||
|
* @param allowedLengths allowed lengths, if any
|
||||||
|
* @return true if value is a non-null, non-empty string of hex digits, and if allowed lengths are given, has
|
||||||
|
* an allowed length
|
||||||
|
*/
|
||||||
|
private static boolean isHexOfLength(CharSequence value, int... allowedLengths) {
|
||||||
|
if (value == null || !HEX_DIGITS.matcher(value).matches()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (allowedLengths.length == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (int length : allowedLengths) {
|
||||||
|
if (value.length() == length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,12 +4,10 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.design.widget.FloatingActionButton;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -18,11 +16,6 @@ import android.widget.ImageView;
|
||||||
import android.widget.SimpleCursorAdapter;
|
import android.widget.SimpleCursorAdapter;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
|
||||||
import net.foucry.pilldroid.Medicament;
|
|
||||||
import net.foucry.pilldroid.dummy.DummyContent;
|
|
||||||
import static net.foucry.pilldroid.UtilDate.*;
|
|
||||||
import static net.foucry.pilldroid.Utils.*;
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -30,6 +23,9 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static net.foucry.pilldroid.UtilDate.date2String;
|
||||||
|
import static net.foucry.pilldroid.Utils.doubleRandomInclusive;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity representing a list of Medicaments. This activity
|
* An activity representing a list of Medicaments. This activity
|
||||||
* has different presentations for handset and tablet-size devices. On
|
* has different presentations for handset and tablet-size devices. On
|
||||||
|
@ -74,8 +70,12 @@ public class MedicamentListActivity extends AppCompatActivity {
|
||||||
fab.setOnClickListener(new View.OnClickListener() {
|
fab.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
Snackbar.make(view, "Will be used to add a drug to the list", Snackbar.LENGTH_LONG)
|
/* Snackbar.make(view, "Will be used to add a drug to the list", Snackbar.LENGTH_LONG)
|
||||||
.setAction("Action", null).show();
|
.setAction("Action", null).show(); */
|
||||||
|
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
|
||||||
|
intent.putExtra("SCAN_MODE", "CODE_128");
|
||||||
|
//intent.putExtra("SCAN_FORMATS", "EAN_13,DATA_MATRIX");
|
||||||
|
startActivityForResult(intent, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -247,6 +247,27 @@ public class MedicamentListActivity extends AppCompatActivity {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + " '" + mContentView.getText() + "'";
|
return super.toString() + " '" + mContentView.getText() + "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void scanNow(View view) {
|
||||||
|
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
|
||||||
|
intent.putExtra("SCAN_MODE", "CODE_128");
|
||||||
|
//intent.putExtra("SCAN_FORMATS", "EAN_13,DATA_MATRIX");
|
||||||
|
startActivityForResult(intent, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
|
if (requestCode == 0) {
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
String contents = intent.getStringExtra("SCAN_RESULT");
|
||||||
|
String format = intent.getStringExtra("SCAN_RESULT_FORMAT");
|
||||||
|
Log.i("Prout", format);
|
||||||
|
Log.i("Prout", contents);
|
||||||
|
// Handle successful scan
|
||||||
|
} else if (resultCode == RESULT_CANCELED) {
|
||||||
|
// Handle cancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
app/src/main/res/drawable/share_via_barcode.png
Normal file
BIN
app/src/main/res/drawable/share_via_barcode.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
|
@ -36,6 +36,7 @@
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_gravity="bottom|end"
|
android:layout_gravity="bottom|end"
|
||||||
android:layout_margin="@dimen/fab_margin"
|
android:layout_margin="@dimen/fab_margin"
|
||||||
|
android:onClick="scanNow"
|
||||||
android:src="@android:drawable/ic_input_add"
|
android:src="@android:drawable/ic_input_add"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
|
|
38
app/src/main/res/layout/app_picker_list_item.xml
Normal file
38
app/src/main/res/layout/app_picker_list_item.xml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/app_picker_list_item_icon"
|
||||||
|
android:layout_width="64dip"
|
||||||
|
android:layout_height="64dip"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:padding="@dimen/half_padding"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/app_picker_list_item_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:padding="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
38
app/src/main/res/layout/bookmark_picker_list_item.xml
Normal file
38
app/src/main/res/layout/bookmark_picker_list_item.xml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/half_padding">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/bookmark_title"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textIsSelectable="false"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/bookmark_url"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:textIsSelectable="false"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
204
app/src/main/res/layout/capture.xml
Normal file
204
app/src/main/res/layout/capture.xml
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
<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"/>
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/result_view"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:background="@color/result_view"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:gravity="right|center_vertical">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/barcode_image_view"
|
||||||
|
android:layout_width="160dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxWidth="160dip"
|
||||||
|
android:maxHeight="160dip"
|
||||||
|
android:layout_marginBottom="@dimen/half_padding"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_format"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/format_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_type"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/type_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_time"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/time_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/meta_text_view_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_meta"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/meta_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/contents_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_text"
|
||||||
|
android:textColorLink="@color/result_text"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:paddingLeft="12dip"
|
||||||
|
android:autoLink="web"
|
||||||
|
android:textIsSelectable="true"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/contents_supplement_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_text"
|
||||||
|
android:textColorLink="@color/result_text"
|
||||||
|
android:paddingLeft="12dip"
|
||||||
|
android:autoLink="web"
|
||||||
|
android:textIsSelectable="true"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/result_button_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<Button style="@style/ResultButton"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button style="@style/ResultButton"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button style="@style/ResultButton"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button style="@style/ResultButton"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/status_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|center_horizontal"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:text="@string/msg_default_status"
|
||||||
|
android:textColor="@color/status_text"/>
|
||||||
|
|
||||||
|
</merge>
|
53
app/src/main/res/layout/encode.xml
Normal file
53
app/src/main/res/layout/encode.xml
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:background="@color/encode_view"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
tools:ignore="Overdraw">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/image_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:scaleType="center"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<ScrollView android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/contents_text_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/contents_text"
|
||||||
|
android:paddingBottom="@dimen/standard_padding"
|
||||||
|
android:paddingLeft="@dimen/standard_padding"
|
||||||
|
android:paddingRight="@dimen/standard_padding"
|
||||||
|
android:textIsSelectable="true"/>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
21
app/src/main/res/layout/help.xml
Normal file
21
app/src/main/res/layout/help.xml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/help_contents"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:layout_weight="1"/>
|
38
app/src/main/res/layout/history_list_item.xml
Normal file
38
app/src/main/res/layout/history_list_item.xml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/history_title"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textIsSelectable="false"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/history_detail"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:textIsSelectable="false"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
54
app/src/main/res/layout/search_book_contents.xml
Normal file
54
app/src/main/res/layout/search_book_contents.xml
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0">
|
||||||
|
|
||||||
|
<EditText android:id="@+id/query_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left|center_vertical"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:selectAllOnFocus="true"
|
||||||
|
android:inputType="text"
|
||||||
|
tools:ignore="NestedWeights"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/query_button"
|
||||||
|
style="@android:style/Widget.Holo.Button.Borderless.Small"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:text="@string/button_search_book_contents"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<ListView android:id="@+id/result_list_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
24
app/src/main/res/layout/search_book_contents_header.xml
Normal file
24
app/src/main/res/layout/search_book_contents_header.xml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="@dimen/standard_padding"
|
||||||
|
android:paddingBottom="@dimen/half_padding"
|
||||||
|
android:enabled="false"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textIsSelectable="false"/>
|
40
app/src/main/res/layout/search_book_contents_list_item.xml
Normal file
40
app/src/main/res/layout/search_book_contents_list_item.xml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
<com.google.zxing.client.android.book.SearchBookContentsListItem
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/page_number_view"
|
||||||
|
android:layout_width="75dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left|top"
|
||||||
|
android:layout_marginRight="@dimen/standard_padding"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textIsSelectable="false"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/snippet_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left|top"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:textIsSelectable="false"/>
|
||||||
|
|
||||||
|
</com.google.zxing.client.android.book.SearchBookContentsListItem>
|
95
app/src/main/res/layout/share.xml
Normal file
95
app/src/main/res/layout/share.xml
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- ScrollView wrapper is to accommodate small screens. -->
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<!-- Must wrap the rest in one layout -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/msg_share_explanation"
|
||||||
|
android:paddingBottom="@dimen/standard_padding"
|
||||||
|
android:textIsSelectable="false"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/share_via_barcode"
|
||||||
|
android:paddingBottom="@dimen/standard_padding"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingBottom="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_app_button"
|
||||||
|
style="@style/ShareButton"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/button_share_app"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_bookmark_button"
|
||||||
|
style="@style/ShareButton"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/button_share_bookmark"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingBottom="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_contact_button"
|
||||||
|
style="@style/ShareButton"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/button_share_contact"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_clipboard_button"
|
||||||
|
style="@style/ShareButton"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/button_share_clipboard"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText android:id="@+id/share_text_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/msg_share_text"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:selectAllOnFocus="true"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
38
app/src/main/res/menu/capture.xml
Normal file
38
app/src/main/res/menu/capture.xml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/menu_share"
|
||||||
|
android:title="@string/menu_share"
|
||||||
|
android:icon="@android:drawable/ic_menu_share"
|
||||||
|
android:orderInCategory="1"
|
||||||
|
android:showAsAction="withText|ifRoom"/>
|
||||||
|
<item android:id="@+id/menu_history"
|
||||||
|
android:title="@string/menu_history"
|
||||||
|
android:icon="@android:drawable/ic_menu_recent_history"
|
||||||
|
android:orderInCategory="2"
|
||||||
|
android:showAsAction="withText|ifRoom"/>
|
||||||
|
<item android:id="@+id/menu_settings"
|
||||||
|
android:title="@string/menu_settings"
|
||||||
|
android:icon="@android:drawable/ic_menu_preferences"
|
||||||
|
android:orderInCategory="3"
|
||||||
|
android:showAsAction="withText"/>
|
||||||
|
<item android:id="@+id/menu_help"
|
||||||
|
android:title="@string/menu_help"
|
||||||
|
android:icon="@android:drawable/ic_menu_help"
|
||||||
|
android:orderInCategory="4"
|
||||||
|
android:showAsAction="withText"/>
|
||||||
|
</menu>
|
28
app/src/main/res/menu/encode.xml
Normal file
28
app/src/main/res/menu/encode.xml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/menu_share"
|
||||||
|
android:title="@string/menu_share"
|
||||||
|
android:icon="@android:drawable/ic_menu_share"
|
||||||
|
android:orderInCategory="1"
|
||||||
|
android:showAsAction="withText|ifRoom"/>
|
||||||
|
<item android:id="@+id/menu_encode"
|
||||||
|
android:title="@string/menu_encode_vcard"
|
||||||
|
android:icon="@android:drawable/ic_menu_sort_alphabetically"
|
||||||
|
android:orderInCategory="2"
|
||||||
|
android:showAsAction="withText|ifRoom"/>
|
||||||
|
</menu>
|
28
app/src/main/res/menu/history.xml
Normal file
28
app/src/main/res/menu/history.xml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/menu_history_send"
|
||||||
|
android:title="@string/history_send"
|
||||||
|
android:icon="@android:drawable/ic_menu_share"
|
||||||
|
android:orderInCategory="1"
|
||||||
|
android:showAsAction="withText|ifRoom"/>
|
||||||
|
<item android:id="@+id/menu_history_clear_text"
|
||||||
|
android:title="@string/history_clear_text"
|
||||||
|
android:icon="@android:drawable/ic_menu_delete"
|
||||||
|
android:orderInCategory="2"
|
||||||
|
android:showAsAction="withText|ifRoom"/>
|
||||||
|
</menu>
|
BIN
app/src/main/res/raw/beep.ogg
Normal file
BIN
app/src/main/res/raw/beep.ogg
Normal file
Binary file not shown.
62
app/src/main/res/values/arrays.xml
Normal file
62
app/src/main/res/values/arrays.xml
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?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>
|
|
@ -3,4 +3,15 @@
|
||||||
<color name="colorPrimary">#3F51B5</color>
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
<color name="colorPrimaryDark">#303F9F</color>
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
<color name="colorAccent">#FF4081</color>
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
<color name="contents_text">#ff000000</color>
|
||||||
|
<color name="encode_view">#ffffffff</color>
|
||||||
|
<color name="possible_result_points">#c0ffbd21</color> <!-- Android standard ICS color -->
|
||||||
|
<color name="result_minor_text">#ffc0c0c0</color>
|
||||||
|
<color name="result_points">#c099cc00</color> <!-- Android standard ICS color -->
|
||||||
|
<color name="result_text">#ffffffff</color>
|
||||||
|
<color name="result_view">#b0000000</color>
|
||||||
|
<color name="status_text">#ffffffff</color>
|
||||||
|
<color name="transparent">#00000000</color>
|
||||||
|
<color name="viewfinder_laser">#ffcc0000</color> <!-- Android standard ICS color -->
|
||||||
|
<color name="viewfinder_mask">#60000000</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -4,4 +4,9 @@
|
||||||
<dimen name="app_bar_height">75dp</dimen>
|
<dimen name="app_bar_height">75dp</dimen>
|
||||||
<dimen name="item_width">200dp</dimen>
|
<dimen name="item_width">200dp</dimen>
|
||||||
<dimen name="text_margin">16dp</dimen>
|
<dimen name="text_margin">16dp</dimen>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
<dimen name="standard_padding">8dip</dimen>
|
||||||
|
<dimen name="half_padding">4dip</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
25
app/src/main/res/values/ids.xml
Normal file
25
app/src/main/res/values/ids.xml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?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>
|
||||||
|
<item type="id" name="decode"/>
|
||||||
|
<item type="id" name="decode_failed"/>
|
||||||
|
<item type="id" name="decode_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"/>
|
||||||
|
</resources>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue