diff --git a/main/AndroidManifest.xml b/main/AndroidManifest.xml
new file mode 100644
index 0000000..3c81179
--- /dev/null
+++ b/main/AndroidManifest.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/java/com/dommy/qrcode/util/BitmapUtil.java b/main/java/com/dommy/qrcode/util/BitmapUtil.java
new file mode 100644
index 0000000..8adaa43
--- /dev/null
+++ b/main/java/com/dommy/qrcode/util/BitmapUtil.java
@@ -0,0 +1,115 @@
+package com.dommy.qrcode.util;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Bitmap util.
+ *
从Uri直接读取图片流,避免路径转换的适配问题
+ */
+public class BitmapUtil {
+ /**
+ * 读取一个缩放后的图片,限定图片大小,避免OOM
+ *
+ * @param uri 图片uri,支持“file://”、“content://”
+ * @param maxWidth 最大允许宽度
+ * @param maxHeight 最大允许高度
+ * @return 返回一个缩放后的Bitmap,失败则返回null
+ */
+ public static Bitmap decodeUri(Context context, Uri uri, int maxWidth, int maxHeight) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true; //只读取图片尺寸
+ readBitmapScale(context, uri, options);
+
+ //计算实际缩放比例
+ int scale = 1;
+ for (int i = 0; i < Integer.MAX_VALUE; i++) {
+ if ((options.outWidth / scale > maxWidth &&
+ options.outWidth / scale > maxWidth * 1.4) ||
+ (options.outHeight / scale > maxHeight &&
+ options.outHeight / scale > maxHeight * 1.4)) {
+ scale++;
+ } else {
+ break;
+ }
+ }
+
+ options.inSampleSize = scale;
+ options.inJustDecodeBounds = false;//读取图片内容
+ options.inPreferredConfig = Bitmap.Config.RGB_565; //根据情况进行修改
+ Bitmap bitmap = null;
+ try {
+ bitmap = readBitmapData(context, uri, options);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ return bitmap;
+ }
+
+ private static void readBitmapScale(Context context, Uri uri, BitmapFactory.Options options) {
+ if (uri == null) {
+ return;
+ }
+ String scheme = uri.getScheme();
+ if (ContentResolver.SCHEME_CONTENT.equals(scheme) ||
+ ContentResolver.SCHEME_FILE.equals(scheme)) {
+ InputStream stream = null;
+ try {
+ stream = context.getContentResolver().openInputStream(uri);
+ BitmapFactory.decodeStream(stream, null, options);
+ } catch (Exception e) {
+ Log.w("readBitmapScale", "Unable to open content: " + uri, e);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ Log.e("readBitmapScale", "Unable to close content: " + uri, e);
+ }
+ }
+ }
+ } else if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
+ Log.e("readBitmapScale", "Unable to close content: " + uri);
+ } else {
+ Log.e("readBitmapScale", "Unable to close content: " + uri);
+ }
+ }
+
+ private static Bitmap readBitmapData(Context context, Uri uri, BitmapFactory.Options options) {
+ if (uri == null) {
+ return null;
+ }
+ Bitmap bitmap = null;
+ String scheme = uri.getScheme();
+ if (ContentResolver.SCHEME_CONTENT.equals(scheme) ||
+ ContentResolver.SCHEME_FILE.equals(scheme)) {
+ InputStream stream = null;
+ try {
+ stream = context.getContentResolver().openInputStream(uri);
+ bitmap = BitmapFactory.decodeStream(stream, null, options);
+ } catch (Exception e) {
+ Log.e("readBitmapData", "Unable to open content: " + uri, e);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ Log.e("readBitmapData", "Unable to close content: " + uri, e);
+ }
+ }
+ }
+ } else if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
+ Log.e("readBitmapData", "Unable to close content: " + uri);
+ } else {
+ Log.e("readBitmapData", "Unable to close content: " + uri);
+ }
+ return bitmap;
+ }
+}
\ No newline at end of file
diff --git a/main/java/com/dommy/qrcode/util/Constant.java b/main/java/com/dommy/qrcode/util/Constant.java
new file mode 100644
index 0000000..9a490e0
--- /dev/null
+++ b/main/java/com/dommy/qrcode/util/Constant.java
@@ -0,0 +1,13 @@
+package com.dommy.qrcode.util;
+
+/**
+ * 常量
+ */
+public class Constant {
+ // request参数
+ public static final int REQ_QR_CODE = 11002; // // 打开扫描界面请求码
+ public static final int REQ_PERM_CAMERA = 11003; // 打开摄像头
+ public static final int REQ_PERM_EXTERNAL_STORAGE = 11004; // 读写文件
+
+ public static final String INTENT_EXTRA_KEY_QR_SCAN = "qr_scan_result";
+}
diff --git a/main/java/com/dommy/qrcode/util/UriUtil.java b/main/java/com/dommy/qrcode/util/UriUtil.java
new file mode 100644
index 0000000..6f4cd2c
--- /dev/null
+++ b/main/java/com/dommy/qrcode/util/UriUtil.java
@@ -0,0 +1,95 @@
+package com.dommy.qrcode.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+/**
+ * Uri路径工具
+ *
+ * @Deprecated 该方法存在终端适配问题,较为麻烦,已经弃用,新方法为BitmapUtil
+ * @see BitmapUtil
+ */
+@Deprecated
+public class UriUtil {
+ /**
+ * 根据图片的Uri获取图片的绝对路径(适配多种API)
+ *
+ * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
+ */
+ public static String getRealPathFromUri(Context context, Uri uri) {
+ int sdkVersion = Build.VERSION.SDK_INT;
+ if (sdkVersion < 11) return getRealPathFromUri_BelowApi11(context, uri);
+ if (sdkVersion < 19) return getRealPathFromUri_Api11To18(context, uri);
+ else return getRealPathFromUri_AboveApi19(context, uri);
+ }
+
+ /**
+ * 适配api19以上,根据uri获取图片的绝对路径
+ */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static String getRealPathFromUri_AboveApi19(Context context, Uri uri) {
+ String filePath = null;
+ String wholeID = DocumentsContract.getDocumentId(uri);
+
+ // 使用':'分割
+ String[] ids = wholeID.split(":");
+ String id = null;
+ if (ids == null) {
+ return null;
+ }
+ if (ids.length > 1) {
+ id = ids[1];
+ } else {
+ id = ids[0];
+ }
+
+ String[] projection = {MediaStore.Images.Media.DATA};
+ String selection = MediaStore.Images.Media._ID + "=?";
+ String[] selectionArgs = {id};
+
+ Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,//
+ projection, selection, selectionArgs, null);
+ int columnIndex = cursor.getColumnIndex(projection[0]);
+ if (cursor.moveToFirst()) filePath = cursor.getString(columnIndex);
+ cursor.close();
+ return filePath;
+ }
+
+ /**
+ * 适配api11-api18,根据uri获取图片的绝对路径
+ */
+ private static String getRealPathFromUri_Api11To18(Context context, Uri uri) {
+ String filePath = null;
+ String[] projection = {MediaStore.Images.Media.DATA};
+ CursorLoader loader = new CursorLoader(context, uri, projection, null, null, null);
+ Cursor cursor = loader.loadInBackground();
+
+ if (cursor != null) {
+ cursor.moveToFirst();
+ filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+ cursor.close();
+ }
+ return filePath;
+ }
+
+ /**
+ * 适配api11以下(不包括api11),根据uri获取图片的绝对路径
+ */
+ private static String getRealPathFromUri_BelowApi11(Context context, Uri uri) {
+ String filePath = null;
+ String[] projection = {MediaStore.Images.Media.DATA};
+ Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
+ if (cursor != null) {
+ cursor.moveToFirst();
+ filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+ cursor.close();
+ }
+ return filePath;
+ }
+}
diff --git a/main/java/com/geogle/zxing/activity/CaptureActivity.java b/main/java/com/geogle/zxing/activity/CaptureActivity.java
new file mode 100644
index 0000000..e26e6ce
--- /dev/null
+++ b/main/java/com/geogle/zxing/activity/CaptureActivity.java
@@ -0,0 +1,385 @@
+package com.geogle.zxing.activity;
+
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Vibrator;
+
+import android.text.TextUtils;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.ChecksumException;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.FormatException;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.Result;
+import com.geogle.zxing.camera.CameraManager;
+import com.google.zxing.common.HybridBinarizer;
+
+import com.geogle.zxing.decoding.InactivityTimer;
+import com.geogle.zxing.decoding.RGBLuminanceSource;
+import com.google.zxing.qrcode.QRCodeReader;
+import com.geogle.zxing.view.ViewfinderView;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import com.geogle.zxing.decoding.CaptureActivityHandler;
+import edu.hzuapps.androidlabs.R;
+
+
+/**
+ * Initial the camera
+ *
+ * @author Ryan.Tang
+ */
+public class CaptureActivity extends AppCompatActivity implements Callback {
+
+ private static final int REQUEST_CODE_SCAN_GALLERY = 100;
+
+ private CaptureActivityHandler handler;
+ private ViewfinderView viewfinderView;
+ private ImageButton back;
+ private ImageButton btnFlash;
+ private Button btnAlbum; // 相册
+ private boolean isFlashOn = false;
+ private boolean hasSurface;
+ private Vector decodeFormats;
+ private String characterSet;
+ private InactivityTimer inactivityTimer;
+ private MediaPlayer mediaPlayer;
+ private boolean playBeep;
+ private static final float BEEP_VOLUME = 0.10f;
+ private boolean vibrate;
+ private ProgressDialog mProgress;
+ private String photo_path;
+ private Bitmap scanBitmap;
+ // private Button cancelScanButton;
+ /**
+ * Called when the activity is first created.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_scanner);
+ //ViewUtil.addTopView(getApplicationContext(), this, R.string.scan_card);
+ CameraManager.init(getApplication());
+ viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_content);
+ back = (ImageButton) findViewById(R.id.btn_back);
+ back.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+
+ btnFlash = (ImageButton) findViewById(R.id.btn_flash);
+ btnFlash.setOnClickListener(flashListener);
+
+ btnAlbum = (Button) findViewById(R.id.btn_album);
+ btnAlbum.setOnClickListener(albumOnClick);
+
+// cancelScanButton = (Button) this.findViewById(R.id.btn_cancel_scan);
+ hasSurface = false;
+ inactivityTimer = new InactivityTimer(this);
+
+ }
+
+ private View.OnClickListener albumOnClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ //打开手机中的相册
+ Intent innerIntent = new Intent(Intent.ACTION_GET_CONTENT); //"android.intent.action.GET_CONTENT"
+ innerIntent.setType("image/*");
+ startActivityForResult(innerIntent, REQUEST_CODE_SCAN_GALLERY);
+ }
+ };
+
+
+ @Override
+ protected void onActivityResult(final int requestCode, int resultCode, Intent data) {
+ if (resultCode==RESULT_OK) {
+ switch (requestCode) {
+ case REQUEST_CODE_SCAN_GALLERY:
+ handleAlbumPic(data);
+ break;
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ /**
+ * 处理选择的图片
+ * @param data
+ */
+ private void handleAlbumPic(Intent data) {
+ //获取选中图片的路径
+ final Uri uri = data.getData();
+
+ mProgress = new ProgressDialog(CaptureActivity.this);
+ mProgress.setMessage("正在扫描...");
+ mProgress.setCancelable(false);
+ mProgress.show();
+
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Result result = scanningImage(uri);
+ mProgress.dismiss();
+ if (result != null) {
+ Intent resultIntent = new Intent();
+ Bundle bundle = new Bundle();
+ bundle.putString(com.dommy.qrcode.util.Constant.INTENT_EXTRA_KEY_QR_SCAN ,result.getText());
+
+ resultIntent.putExtras(bundle);
+ CaptureActivity.this.setResult(RESULT_OK, resultIntent);
+ finish();
+ } else {
+ Toast.makeText(CaptureActivity.this, "识别失败", Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+
+ /**
+ * 扫描二维码图片的方法
+ * @param uri
+ * @return
+ */
+ public Result scanningImage(Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+ Hashtable hints = new Hashtable<>();
+ hints.put(DecodeHintType.CHARACTER_SET, "UTF8"); //设置二维码内容的编码
+
+ scanBitmap = com.dommy.qrcode.util.BitmapUtil.decodeUri(this, uri, 500, 500);
+ RGBLuminanceSource source = new RGBLuminanceSource(scanBitmap);
+ BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
+ QRCodeReader reader = new QRCodeReader();
+ try {
+ return reader.decode(bitmap1, hints);
+ } catch (NotFoundException e) {
+ e.printStackTrace();
+ } catch (ChecksumException e) {
+ e.printStackTrace();
+ } catch (FormatException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ SurfaceView surfaceView = (SurfaceView) findViewById(R.id.scanner_view);
+ SurfaceHolder surfaceHolder = surfaceView.getHolder();
+ if (hasSurface) {
+ initCamera(surfaceHolder);
+ } else {
+ surfaceHolder.addCallback(this);
+ surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ }
+ decodeFormats = null;
+ characterSet = null;
+
+ playBeep = true;
+ AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
+ if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
+ playBeep = false;
+ }
+ initBeepSound();
+ vibrate = true;
+
+ //quit the scan view
+// cancelScanButton.setOnClickListener(new OnClickListener() {
+//
+// @Override
+// public void onClick(View v) {
+// CaptureActivity.this.finish();
+// }
+// });
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (handler != null) {
+ handler.quitSynchronously();
+ handler = null;
+ }
+ CameraManager.get().closeDriver();
+ }
+
+ @Override
+ protected void onDestroy() {
+ inactivityTimer.shutdown();
+ super.onDestroy();
+ }
+
+ /**
+ * Handler scan result
+ *
+ * @param result
+ * @param barcode
+ */
+ public void handleDecode(Result result, Bitmap barcode) {
+ inactivityTimer.onActivity();
+ playBeepSoundAndVibrate();
+ String resultString = result.getText();
+ //FIXME
+ if (TextUtils.isEmpty(resultString)) {
+ Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
+ } else {
+ Intent resultIntent = new Intent();
+ Bundle bundle = new Bundle();
+ bundle.putString(com.dommy.qrcode.util.Constant.INTENT_EXTRA_KEY_QR_SCAN, resultString);
+ System.out.println("sssssssssssssssss scan 0 = " + resultString);
+ // 不能使用Intent传递大于40kb的bitmap,可以使用一个单例对象存储这个bitmap
+// bundle.putParcelable("bitmap", barcode);
+// Logger.d("saomiao",resultString);
+ resultIntent.putExtras(bundle);
+ this.setResult(RESULT_OK, resultIntent);
+ }
+ CaptureActivity.this.finish();
+ }
+
+ private void initCamera(SurfaceHolder surfaceHolder) {
+ try {
+ CameraManager.get().openDriver(surfaceHolder);
+ } catch (IOException ioe) {
+ return;
+ } catch (RuntimeException e) {
+ return;
+ }
+ if (handler == null) {
+ handler = new CaptureActivityHandler(this, decodeFormats,
+ characterSet);
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {
+
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ if (!hasSurface) {
+ hasSurface = true;
+ initCamera(holder);
+ }
+
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ hasSurface = false;
+
+ }
+
+ public ViewfinderView getViewfinderView() {
+ return viewfinderView;
+ }
+
+ public Handler getHandler() {
+ return handler;
+ }
+
+ public void drawViewfinder() {
+ viewfinderView.drawViewfinder();
+
+ }
+
+ private void initBeepSound() {
+ 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.
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ mediaPlayer = new MediaPlayer();
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mediaPlayer.setOnCompletionListener(beepListener);
+
+ AssetFileDescriptor file = getResources().openRawResourceFd(
+ R.raw.beep);
+ try {
+ mediaPlayer.setDataSource(file.getFileDescriptor(),
+ file.getStartOffset(), file.getLength());
+ file.close();
+ mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
+ mediaPlayer.prepare();
+ } catch (IOException e) {
+ mediaPlayer = null;
+ }
+ }
+ }
+
+ private static final long VIBRATE_DURATION = 200L;
+
+ private void playBeepSoundAndVibrate() {
+ if (playBeep && mediaPlayer != null) {
+ mediaPlayer.start();
+ }
+ if (vibrate) {
+ Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
+ vibrator.vibrate(VIBRATE_DURATION);
+ }
+ }
+
+ /**
+ * When the beep has finished playing, rewind to queue up another one.
+ */
+ private final OnCompletionListener beepListener = new OnCompletionListener() {
+ public void onCompletion(MediaPlayer mediaPlayer) {
+ mediaPlayer.seekTo(0);
+ }
+ };
+
+ /**
+ * 闪光灯开关按钮
+ */
+ private View.OnClickListener flashListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ try {
+ boolean isSuccess = CameraManager.get().setFlashLight(!isFlashOn);
+ if(!isSuccess){
+ Toast.makeText(CaptureActivity.this, "暂时无法开启闪光灯", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (isFlashOn) {
+ // 关闭闪光灯
+ btnFlash.setImageResource(R.drawable.flash_off);
+ isFlashOn = false;
+ } else {
+ // 开启闪光灯
+ btnFlash.setImageResource(R.drawable.flash_on);
+ isFlashOn = true;
+ }
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+ };
+}
\ No newline at end of file
diff --git a/main/java/com/geogle/zxing/camera/AutoFocusCallback.java b/main/java/com/geogle/zxing/camera/AutoFocusCallback.java
new file mode 100644
index 0000000..7f118bd
--- /dev/null
+++ b/main/java/com/geogle/zxing/camera/AutoFocusCallback.java
@@ -0,0 +1,48 @@
+/*
+ * 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.geogle.zxing.camera;
+
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+final class AutoFocusCallback implements Camera.AutoFocusCallback {
+
+ private static final String TAG = AutoFocusCallback.class.getSimpleName();
+
+ private static final long AUTOFOCUS_INTERVAL_MS = 1500L;
+
+ private Handler autoFocusHandler;
+ private int autoFocusMessage;
+
+ void setHandler(Handler autoFocusHandler, int autoFocusMessage) {
+ this.autoFocusHandler = autoFocusHandler;
+ this.autoFocusMessage = autoFocusMessage;
+ }
+
+ public void onAutoFocus(boolean success, Camera camera) {
+ if (autoFocusHandler != null) {
+ Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
+ autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
+ autoFocusHandler = null;
+ } else {
+ Log.d(TAG, "Got auto-focus callback, but no handler for it");
+ }
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/camera/CameraConfigurationManager.java b/main/java/com/geogle/zxing/camera/CameraConfigurationManager.java
new file mode 100644
index 0000000..ae66cc2
--- /dev/null
+++ b/main/java/com/geogle/zxing/camera/CameraConfigurationManager.java
@@ -0,0 +1,278 @@
+/*
+ * 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.geogle.zxing.camera;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.os.Build;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+import java.util.regex.Pattern;
+
+final class CameraConfigurationManager {
+
+ private static final String TAG = CameraConfigurationManager.class.getSimpleName();
+
+ private static final int TEN_DESIRED_ZOOM = 27;
+ private static final int DESIRED_SHARPNESS = 30;
+
+ private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+ private final Context context;
+ private Point screenResolution;
+ private Point cameraResolution;
+ private int previewFormat;
+ private String previewFormatString;
+
+ CameraConfigurationManager(Context context) {
+ this.context = context;
+ }
+
+ /**
+ * Reads, one time, values from the camera that are needed by the app.
+ */
+ void initFromCameraParameters(Camera camera) {
+ Camera.Parameters parameters = camera.getParameters();
+ previewFormat = parameters.getPreviewFormat();
+ previewFormatString = parameters.get("preview-format");
+ Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
+ WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = manager.getDefaultDisplay();
+ screenResolution = new Point(display.getWidth(), display.getHeight());
+ Log.d(TAG, "Screen resolution: " + screenResolution);
+
+ //图片拉伸
+ Point screenResolutionForCamera = new Point();
+ screenResolutionForCamera.x = screenResolution.x;
+ screenResolutionForCamera.y = screenResolution.y;
+ // preview size is always something like 480*320, other 320*480
+ if (screenResolution.x < screenResolution.y) {
+ screenResolutionForCamera.x = screenResolution.y;
+ screenResolutionForCamera.y = screenResolution.x;
+ }
+
+ cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);
+ Log.d(TAG, "Camera resolution: " + screenResolution);
+
+ }
+
+ /**
+ * Sets the camera up to take preview images which are used for both preview and decoding.
+ * We detect the preview format here so that buildLuminanceSource() can build an appropriate
+ * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
+ * and the planar Y can be used for barcode scanning without a copy in some cases.
+ */
+ void setDesiredCameraParameters(Camera camera) {
+ Camera.Parameters parameters = camera.getParameters();
+ Log.d(TAG, "Setting preview size: " + cameraResolution);
+ parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
+ setFlash(parameters);
+ setZoom(parameters);
+ //setSharpness(parameters);
+ //modify here
+ camera.setDisplayOrientation(90);
+ camera.setParameters(parameters);
+ }
+
+ Point getCameraResolution() {
+ return cameraResolution;
+ }
+
+ Point getScreenResolution() {
+ return screenResolution;
+ }
+
+ int getPreviewFormat() {
+ return previewFormat;
+ }
+
+ String getPreviewFormatString() {
+ return previewFormatString;
+ }
+
+ private static Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) {
+
+ String previewSizeValueString = parameters.get("preview-size-values");
+ // saw this on Xperia
+ if (previewSizeValueString == null) {
+ previewSizeValueString = parameters.get("preview-size-value");
+ }
+
+ Point cameraResolution = null;
+
+ if (previewSizeValueString != null) {
+ Log.d(TAG, "preview-size-values parameter: " + previewSizeValueString);
+ cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
+ }
+
+ if (cameraResolution == null) {
+ // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
+ cameraResolution = new Point(
+ (screenResolution.x >> 3) << 3,
+ (screenResolution.y >> 3) << 3);
+ }
+
+ return cameraResolution;
+ }
+
+ private static Point findBestPreviewSizeValue(CharSequence previewSizeValueString, Point screenResolution) {
+ int bestX = 0;
+ int bestY = 0;
+ int diff = Integer.MAX_VALUE;
+ for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
+
+ previewSize = previewSize.trim();
+ int dimPosition = previewSize.indexOf('x');
+ if (dimPosition < 0) {
+ Log.w(TAG, "Bad preview-size: " + previewSize);
+ continue;
+ }
+
+ int newX;
+ int newY;
+ try {
+ newX = Integer.parseInt(previewSize.substring(0, dimPosition));
+ newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
+ } catch (NumberFormatException nfe) {
+ Log.w(TAG, "Bad preview-size: " + previewSize);
+ continue;
+ }
+
+ int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
+ if (newDiff == 0) {
+ bestX = newX;
+ bestY = newY;
+ break;
+ } else if (newDiff < diff) {
+ bestX = newX;
+ bestY = newY;
+ diff = newDiff;
+ }
+
+ }
+
+ if (bestX > 0 && bestY > 0) {
+ return new Point(bestX, bestY);
+ }
+ return null;
+ }
+
+ private static int findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) {
+ int tenBestValue = 0;
+ for (String stringValue : COMMA_PATTERN.split(stringValues)) {
+ stringValue = stringValue.trim();
+ double value;
+ try {
+ value = Double.parseDouble(stringValue);
+ } catch (NumberFormatException nfe) {
+ return tenDesiredZoom;
+ }
+ int tenValue = (int) (10.0 * value);
+ if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
+ tenBestValue = tenValue;
+ }
+ }
+ return tenBestValue;
+ }
+
+ private void setFlash(Camera.Parameters parameters) {
+ // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
+ // And this is a hack-hack to work around a different value on the Behold II
+ // Restrict Behold II check to Cupcake, per Samsung's advice
+ //if (Build.MODEL.contains("Behold II") &&
+ // CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) {
+ if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) { // 3 = Cupcake
+ parameters.set("flash-value", 1);
+ } else {
+ parameters.set("flash-value", 2);
+ }
+ // This is the standard setting to turn the flash off that all devices should honor.
+ parameters.set("flash-mode", "off");
+ }
+
+ private void setZoom(Camera.Parameters parameters) {
+
+ String zoomSupportedString = parameters.get("zoom-supported");
+ if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
+ return;
+ }
+
+ int tenDesiredZoom = TEN_DESIRED_ZOOM;
+
+ String maxZoomString = parameters.get("max-zoom");
+ if (maxZoomString != null) {
+ try {
+ int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
+ if (tenDesiredZoom > tenMaxZoom) {
+ tenDesiredZoom = tenMaxZoom;
+ }
+ } catch (NumberFormatException nfe) {
+ Log.w(TAG, "Bad max-zoom: " + maxZoomString);
+ }
+ }
+
+ String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
+ if (takingPictureZoomMaxString != null) {
+ try {
+ int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
+ if (tenDesiredZoom > tenMaxZoom) {
+ tenDesiredZoom = tenMaxZoom;
+ }
+ } catch (NumberFormatException nfe) {
+ Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
+ }
+ }
+
+ String motZoomValuesString = parameters.get("mot-zoom-values");
+ if (motZoomValuesString != null) {
+ tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
+ }
+
+ String motZoomStepString = parameters.get("mot-zoom-step");
+ if (motZoomStepString != null) {
+ try {
+ double motZoomStep = Double.parseDouble(motZoomStepString.trim());
+ int tenZoomStep = (int) (10.0 * motZoomStep);
+ if (tenZoomStep > 1) {
+ tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
+ }
+ } catch (NumberFormatException nfe) {
+ // continue
+ }
+ }
+
+ // Set zoom. This helps encourage the user to pull back.
+ // Some devices like the Behold have a zoom parameter
+ if (maxZoomString != null || motZoomValuesString != null) {
+ parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
+ }
+
+ // Most devices, like the Hero, appear to expose this zoom parameter.
+ // It takes on values like "27" which appears to mean 2.7x zoom
+ if (takingPictureZoomMaxString != null) {
+ parameters.set("taking-picture-zoom", tenDesiredZoom);
+ }
+ }
+
+ public static int getDesiredSharpness() {
+ return DESIRED_SHARPNESS;
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/camera/CameraManager.java b/main/java/com/geogle/zxing/camera/CameraManager.java
new file mode 100644
index 0000000..1366466
--- /dev/null
+++ b/main/java/com/geogle/zxing/camera/CameraManager.java
@@ -0,0 +1,398 @@
+/*
+ * 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.geogle.zxing.camera;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.Camera;
+import android.os.Build;
+import android.os.Handler;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * 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.
+ */
+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 = 480;
+ private static final int MAX_FRAME_HEIGHT = 360;
+
+ private static CameraManager cameraManager;
+
+ static final int SDK_INT; // Later we can use Build.VERSION.SDK_INT
+
+ static {
+ int sdkInt;
+ try {
+ sdkInt = Integer.parseInt(Build.VERSION.SDK);
+ } catch (NumberFormatException nfe) {
+ // Just to be safe
+ sdkInt = 10000;
+ }
+ SDK_INT = sdkInt;
+ }
+
+ private final Context context;
+ private final CameraConfigurationManager configManager;
+ private Camera camera;
+ private Rect framingRect;
+ private Rect framingRectInPreview;
+ private boolean initialized;
+ private boolean previewing;
+ private final boolean useOneShotPreviewCallback;
+ /**
+ * 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;
+ /**
+ * Autofocus callbacks arrive here, and are dispatched to the Handler which requested them.
+ */
+ private final AutoFocusCallback autoFocusCallback;
+
+ /**
+ * Initializes this static object with the Context of the calling Activity.
+ *
+ * @param context The Activity which wants to use the camera.
+ */
+ public static void init(Context context) {
+ if (cameraManager == null) {
+ cameraManager = new CameraManager(context);
+ }
+ }
+
+ /**
+ * Gets the CameraManager singleton instance.
+ *
+ * @return A reference to the CameraManager singleton.
+ */
+ public static CameraManager get() {
+ return cameraManager;
+ }
+
+ private CameraManager(Context context) {
+
+ this.context = context;
+ this.configManager = new CameraConfigurationManager(context);
+
+ // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
+ // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
+ // the more efficient one shot callback, as the older one can swamp the system and cause it
+ // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
+ //useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE;
+ useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3; // 3 = Cupcake
+
+ previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback);
+ autoFocusCallback = new AutoFocusCallback();
+ }
+
+ /**
+ * 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 void openDriver(SurfaceHolder holder) throws IOException {
+ if (camera == null) {
+ camera = Camera.open();
+ if (camera == null) {
+ throw new IOException();
+ }
+ camera.setPreviewDisplay(holder);
+
+ if (!initialized) {
+ initialized = true;
+ configManager.initFromCameraParameters(camera);
+ }
+ configManager.setDesiredCameraParameters(camera);
+
+ //FIXME
+ // SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ //�Ƿ�ʹ��ǰ��
+// if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
+// FlashlightManager.enableFlashlight();
+// }
+ FlashlightManager.enableFlashlight();
+ }
+ }
+
+ /**
+ * Closes the camera driver if still in use.
+ */
+ public void closeDriver() {
+ if (camera != null) {
+ FlashlightManager.disableFlashlight();
+ camera.release();
+ camera = null;
+ }
+ }
+
+ /**
+ * Asks the camera hardware to begin drawing preview frames to the screen.
+ */
+ public void startPreview() {
+ if (camera != null && !previewing) {
+ camera.startPreview();
+ previewing = true;
+ }
+ }
+
+ /**
+ * Tells the camera to stop drawing preview frames.
+ */
+ public void stopPreview() {
+ if (camera != null && previewing) {
+ if (!useOneShotPreviewCallback) {
+ camera.setPreviewCallback(null);
+ }
+ camera.stopPreview();
+ previewCallback.setHandler(null, 0);
+ autoFocusCallback.setHandler(null, 0);
+ previewing = false;
+ }
+ }
+
+ /**
+ * 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 void requestPreviewFrame(Handler handler, int message) {
+ if (camera != null && previewing) {
+ previewCallback.setHandler(handler, message);
+ if (useOneShotPreviewCallback) {
+ camera.setOneShotPreviewCallback(previewCallback);
+ } else {
+ camera.setPreviewCallback(previewCallback);
+ }
+ }
+ }
+
+ /**
+ * Asks the camera hardware to perform an autofocus.
+ *
+ * @param handler The Handler to notify when the autofocus completes.
+ * @param message The message to deliver.
+ */
+ public void requestAutoFocus(Handler handler, int message) {
+ if (camera != null && previewing) {
+ autoFocusCallback.setHandler(handler, message);
+ //Log.d(TAG, "Requesting auto-focus callback");
+ camera.autoFocus(autoFocusCallback);
+ }
+ }
+
+ /**
+ * 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 Rect getFramingRect() {
+ Point screenResolution = configManager.getScreenResolution();
+ if (screenResolution == null)
+ return null;
+ if (framingRect == null) {
+ if (camera == null) {
+ return null;
+ }
+
+ //修改之后
+ int width = screenResolution.x * 7 / 10;
+ int height = screenResolution.y * 7 / 10;
+
+ if (height >= width) { //竖屏
+ height = width;
+ } else { //黑屏
+ width = height;
+ }
+
+ int leftOffset = (screenResolution.x - width) / 2;
+ int topOffset = (screenResolution.y - height) / 3;
+ framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
+
+ }
+ return framingRect;
+ }
+// public Rect getFramingRect() {
+// Point screenResolution = configManager.getScreenResolution();
+// if (framingRect == null) {
+// if (camera == null) {
+// return null;
+// }
+// int width = screenResolution.x * 3 / 4;
+// if (width < MIN_FRAME_WIDTH) {
+// width = MIN_FRAME_WIDTH;
+// } else if (width > MAX_FRAME_WIDTH) {
+// width = MAX_FRAME_WIDTH;
+// }
+// int height = screenResolution.y * 3 / 4;
+// if (height < MIN_FRAME_HEIGHT) {
+// height = MIN_FRAME_HEIGHT;
+// } else if (height > MAX_FRAME_HEIGHT) {
+// 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;
+// }
+
+ /**
+ * Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
+ * not UI / screen.
+ */
+ public Rect getFramingRectInPreview() {
+ if (framingRectInPreview == null) {
+ Rect rect = new Rect(getFramingRect());
+ Point cameraResolution = configManager.getCameraResolution();
+ Point screenResolution = configManager.getScreenResolution();
+ //modify here
+// rect.left = rect.left * cameraResolution.x / screenResolution.x;
+// rect.right = rect.right * cameraResolution.x / screenResolution.x;
+// rect.top = rect.top * cameraResolution.y / screenResolution.y;
+// rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
+ rect.left = rect.left * cameraResolution.y / screenResolution.x;
+ rect.right = rect.right * cameraResolution.y / screenResolution.x;
+ rect.top = rect.top * cameraResolution.x / screenResolution.y;
+ rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
+ framingRectInPreview = rect;
+ }
+ return framingRectInPreview;
+ }
+
+ /**
+ * Converts the result points from still resolution coordinates to screen coordinates.
+ *
+ * @param points The points returned by the Reader subclass through Result.getResultPoints().
+ * @return An array of Points scaled to the size of the framing rect and offset appropriately
+ * so they can be drawn in screen coordinates.
+ */
+ /*
+ public Point[] convertResultPoints(ResultPoint[] points) {
+ Rect frame = getFramingRectInPreview();
+ int count = points.length;
+ Point[] output = new Point[count];
+ for (int x = 0; x < count; x++) {
+ output[x] = new Point();
+ output[x].x = frame.left + (int) (points[x].getX() + 0.5f);
+ output[x].y = frame.top + (int) (points[x].getY() + 0.5f);
+ }
+ return output;
+ }
+ */
+
+ /**
+ * 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();
+ int previewFormat = configManager.getPreviewFormat();
+ String previewFormatString = configManager.getPreviewFormatString();
+ switch (previewFormat) {
+ // This is the standard Android format which all devices are REQUIRED to support.
+ // In theory, it's the only one we should ever care about.
+ case PixelFormat.YCbCr_420_SP:
+ // This format has never been seen in the wild, but is compatible as we only care
+ // about the Y channel, so allow it.
+ case PixelFormat.YCbCr_422_SP:
+ return new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height);
+ default:
+ // The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
+ // Fortunately, it too has all the Y data up front, so we can read it.
+ if ("yuv420p".equals(previewFormatString)) {
+ return new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height);
+ }
+ }
+ throw new IllegalArgumentException("Unsupported picture format: " +
+ previewFormat + '/' + previewFormatString);
+ }
+
+ public Context getContext() {
+ return context;
+ }
+
+ /**
+ * 打开或关闭闪光灯
+ * @param isOpen 是否开启闪光灯
+ * @return boolean 操作成功/失败。
+ */
+ public boolean setFlashLight(boolean isOpen) {
+ if (camera == null || !previewing) {
+ return false;
+ }
+ Camera.Parameters parameters = camera.getParameters();
+ if (parameters == null) {
+ return false;
+ }
+ List flashModes = parameters.getSupportedFlashModes();
+ // 检查手机是否有闪光灯
+ if (null == flashModes || 0 == flashModes.size()) {
+ // 没有闪光灯则返回
+ return false;
+ }
+ String flashMode = parameters.getFlashMode();
+ if (isOpen) {
+ if (Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
+ return true;
+ }
+ // 开启
+ if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
+ parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
+ camera.setParameters(parameters);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ if (Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
+ return true;
+ }
+ // 关闭
+ if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
+ parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
+ camera.setParameters(parameters);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+}
diff --git a/main/java/com/geogle/zxing/camera/FlashlightManager.java b/main/java/com/geogle/zxing/camera/FlashlightManager.java
new file mode 100644
index 0000000..6fd1041
--- /dev/null
+++ b/main/java/com/geogle/zxing/camera/FlashlightManager.java
@@ -0,0 +1,150 @@
+/*
+ * 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.geogle.zxing.camera;
+
+import android.os.IBinder;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * This class is used to activate the weak light on some camera phones (not flash)
+ * in order to illuminate surfaces for scanning. There is no official way to do this,
+ * but, classes which allow access to this function still exist on some devices.
+ * This therefore proceeds through a great deal of reflection.
+ *
+ * See
+ * http://almondmendoza.com/2009/01/05/changing-the-screen-brightness-programatically/ and
+ *
+ * http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo/DroidLED.java.
+ * Thanks to Ryan Alford for pointing out the availability of this class.
+ */
+final class FlashlightManager {
+
+ private static final String TAG = FlashlightManager.class.getSimpleName();
+
+ private static final Object iHardwareService;
+ private static final Method setFlashEnabledMethod;
+ static {
+ iHardwareService = getHardwareService();
+ setFlashEnabledMethod = getSetFlashEnabledMethod(iHardwareService);
+ if (iHardwareService == null) {
+ Log.v(TAG, "This device does supports control of a flashlight");
+ } else {
+ Log.v(TAG, "This device does not support control of a flashlight");
+ }
+ }
+
+ private FlashlightManager() {
+ }
+
+ /**
+ * �����������ƿ���
+ */
+ //FIXME
+ static void enableFlashlight() {
+ setFlashlight(false);
+ }
+
+ static void disableFlashlight() {
+ setFlashlight(false);
+ }
+
+ private static Object getHardwareService() {
+ Class> serviceManagerClass = maybeForName("android.os.ServiceManager");
+ if (serviceManagerClass == null) {
+ return null;
+ }
+
+ Method getServiceMethod = maybeGetMethod(serviceManagerClass, "getService", String.class);
+ if (getServiceMethod == null) {
+ return null;
+ }
+
+ Object hardwareService = invoke(getServiceMethod, null, "hardware");
+ if (hardwareService == null) {
+ return null;
+ }
+
+ Class> iHardwareServiceStubClass = maybeForName("android.os.IHardwareService$Stub");
+ if (iHardwareServiceStubClass == null) {
+ return null;
+ }
+
+ Method asInterfaceMethod = maybeGetMethod(iHardwareServiceStubClass, "asInterface", IBinder.class);
+ if (asInterfaceMethod == null) {
+ return null;
+ }
+
+ return invoke(asInterfaceMethod, null, hardwareService);
+ }
+
+ private static Method getSetFlashEnabledMethod(Object iHardwareService) {
+ if (iHardwareService == null) {
+ return null;
+ }
+ Class> proxyClass = iHardwareService.getClass();
+ return maybeGetMethod(proxyClass, "setFlashlightEnabled", boolean.class);
+ }
+
+ private static Class> maybeForName(String name) {
+ try {
+ return Class.forName(name);
+ } catch (ClassNotFoundException cnfe) {
+ // OK
+ return null;
+ } catch (RuntimeException re) {
+ Log.w(TAG, "Unexpected error while finding class " + name, re);
+ return null;
+ }
+ }
+
+ private static Method maybeGetMethod(Class> clazz, String name, Class>... argClasses) {
+ try {
+ return clazz.getMethod(name, argClasses);
+ } catch (NoSuchMethodException nsme) {
+ // OK
+ return null;
+ } catch (RuntimeException re) {
+ Log.w(TAG, "Unexpected error while finding method " + name, re);
+ return null;
+ }
+ }
+
+ private static Object invoke(Method method, Object instance, Object... args) {
+ try {
+ return method.invoke(instance, args);
+ } catch (IllegalAccessException e) {
+ Log.w(TAG, "Unexpected error while invoking " + method, e);
+ return null;
+ } catch (InvocationTargetException e) {
+ Log.w(TAG, "Unexpected error while invoking " + method, e.getCause());
+ return null;
+ } catch (RuntimeException re) {
+ Log.w(TAG, "Unexpected error while invoking " + method, re);
+ return null;
+ }
+ }
+
+ private static void setFlashlight(boolean active) {
+ if (iHardwareService != null) {
+ invoke(setFlashEnabledMethod, iHardwareService, active);
+ }
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/camera/PlanarYUVLuminanceSource.java b/main/java/com/geogle/zxing/camera/PlanarYUVLuminanceSource.java
new file mode 100644
index 0000000..c6f34ea
--- /dev/null
+++ b/main/java/com/geogle/zxing/camera/PlanarYUVLuminanceSource.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2009 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.geogle.zxing.camera;
+
+import android.graphics.Bitmap;
+
+import com.google.zxing.LuminanceSource;
+
+/**
+ * This object extends LuminanceSource around an array of YUV data returned from the camera driver,
+ * with the option to crop to a rectangle within the full data. This can be used to exclude
+ * superfluous pixels around the perimeter and speed up decoding.
+ *
+ * It works for any pixel format where the Y channel is planar and appears first, including
+ * YCbCr_420_SP and YCbCr_422_SP.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class PlanarYUVLuminanceSource extends LuminanceSource {
+ private final byte[] yuvData;
+ private final int dataWidth;
+ private final int dataHeight;
+ private final int left;
+ private final int top;
+
+ public PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top,
+ int width, int height) {
+ super(width, height);
+
+ if (left + width > dataWidth || top + height > dataHeight) {
+ throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
+ }
+
+ this.yuvData = yuvData;
+ this.dataWidth = dataWidth;
+ this.dataHeight = dataHeight;
+ this.left = left;
+ this.top = top;
+ }
+
+ @Override
+ public byte[] getRow(int y, byte[] row) {
+ if (y < 0 || y >= getHeight()) {
+ throw new IllegalArgumentException("Requested row is outside the image: " + y);
+ }
+ int width = getWidth();
+ if (row == null || row.length < width) {
+ row = new byte[width];
+ }
+ int offset = (y + top) * dataWidth + left;
+ System.arraycopy(yuvData, offset, row, 0, width);
+ return row;
+ }
+
+ @Override
+ public byte[] getMatrix() {
+ int width = getWidth();
+ int height = getHeight();
+
+ // If the caller asks for the entire underlying image, save the copy and give them the
+ // original data. The docs specifically warn that result.length must be ignored.
+ if (width == dataWidth && height == dataHeight) {
+ return yuvData;
+ }
+
+ int area = width * height;
+ byte[] matrix = new byte[area];
+ int inputOffset = top * dataWidth + left;
+
+ // If the width matches the full width of the underlying data, perform a single copy.
+ if (width == dataWidth) {
+ System.arraycopy(yuvData, inputOffset, matrix, 0, area);
+ return matrix;
+ }
+
+ // Otherwise copy one cropped row at a time.
+ byte[] yuv = yuvData;
+ for (int y = 0; y < height; y++) {
+ int outputOffset = y * width;
+ System.arraycopy(yuv, inputOffset, matrix, outputOffset, width);
+ inputOffset += dataWidth;
+ }
+ return matrix;
+ }
+
+ @Override
+ public boolean isCropSupported() {
+ return true;
+ }
+
+ public int getDataWidth() {
+ return dataWidth;
+ }
+
+ public int getDataHeight() {
+ return dataHeight;
+ }
+
+ public Bitmap renderCroppedGreyscaleBitmap() {
+ int width = getWidth();
+ int height = getHeight();
+ int[] pixels = new int[width * height];
+ byte[] yuv = yuvData;
+ int inputOffset = top * dataWidth + left;
+
+ for (int y = 0; y < height; y++) {
+ int outputOffset = y * width;
+ for (int x = 0; x < width; x++) {
+ int grey = yuv[inputOffset + x] & 0xff;
+ pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
+ }
+ inputOffset += dataWidth;
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return bitmap;
+ }
+}
diff --git a/main/java/com/geogle/zxing/camera/PreviewCallback.java b/main/java/com/geogle/zxing/camera/PreviewCallback.java
new file mode 100644
index 0000000..5fbbddb
--- /dev/null
+++ b/main/java/com/geogle/zxing/camera/PreviewCallback.java
@@ -0,0 +1,59 @@
+/*
+ * 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.geogle.zxing.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 final boolean useOneShotPreviewCallback;
+ private Handler previewHandler;
+ private int previewMessage;
+
+ PreviewCallback(CameraConfigurationManager configManager, boolean useOneShotPreviewCallback) {
+ this.configManager = configManager;
+ this.useOneShotPreviewCallback = useOneShotPreviewCallback;
+ }
+
+ void setHandler(Handler previewHandler, int previewMessage) {
+ this.previewHandler = previewHandler;
+ this.previewMessage = previewMessage;
+ }
+
+ public void onPreviewFrame(byte[] data, Camera camera) {
+ Point cameraResolution = configManager.getCameraResolution();
+ if (!useOneShotPreviewCallback) {
+ camera.setPreviewCallback(null);
+ }
+ if (previewHandler != null) {
+ Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
+ cameraResolution.y, data);
+ message.sendToTarget();
+ previewHandler = null;
+ } else {
+ Log.d(TAG, "Got preview callback, but no handler for it");
+ }
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/decoding/CaptureActivityHandler.java b/main/java/com/geogle/zxing/decoding/CaptureActivityHandler.java
new file mode 100644
index 0000000..0642453
--- /dev/null
+++ b/main/java/com/geogle/zxing/decoding/CaptureActivityHandler.java
@@ -0,0 +1,141 @@
+package com.geogle.zxing.decoding;/*
+ * 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.
+ */
+
+
+
+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 com.google.zxing.BarcodeFormat;
+import com.google.zxing.Result;
+import com.geogle.zxing.camera.CameraManager;
+import com.geogle.zxing.view.ViewfinderResultPointCallback;
+
+import java.util.Vector;
+
+import com.geogle.zxing.activity.CaptureActivity;
+import edu.hzuapps.androidlabs.R;
+
+
+/**
+ * This class handles all the messaging which comprises the state machine for capture.
+ */
+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 enum State {
+ PREVIEW,
+ SUCCESS,
+ DONE
+ }
+
+ public CaptureActivityHandler(CaptureActivity activity, Vector decodeFormats,
+ String characterSet) {
+ this.activity = activity;
+ decodeThread = new DecodeThread(activity, decodeFormats, characterSet,
+ new ViewfinderResultPointCallback(activity.getViewfinderView()));
+ decodeThread.start();
+ state = State.SUCCESS;
+ // Start ourselves capturing previews and decoding.
+ CameraManager.get().startPreview();
+ restartPreviewAndDecode();
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case R.id.auto_focus:
+ //Log.d(TAG, "Got auto-focus message");
+ // When one auto focus pass finishes, start another. This is the closest thing to
+ // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do.
+ if (state == State.PREVIEW) {
+ CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
+ }
+ break;
+ case R.id.restart_preview:
+ Log.d(TAG, "Got restart preview message");
+ restartPreviewAndDecode();
+ break;
+ case R.id.decode_succeeded:
+ Log.d(TAG, "Got decode succeeded message");
+ state = State.SUCCESS;
+ Bundle bundle = message.getData();
+
+ /***********************************************************************/
+ Bitmap barcode = bundle == null ? null :
+ (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP);//���ñ����߳�
+
+ activity.handleDecode((Result) message.obj, barcode);//���ؽ��
+ /***********************************************************************/
+ break;
+ case R.id.decode_failed:
+ // We're decoding as fast as possible, so when one decode fails, start another.
+ state = State.PREVIEW;
+ CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
+ break;
+ case R.id.return_scan_result:
+ Log.d(TAG, "Got return scan result message");
+ activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
+ activity.finish();
+ break;
+ case R.id.launch_product_query:
+ Log.d(TAG, "Got product query message");
+ String url = (String) message.obj;
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ activity.startActivity(intent);
+ break;
+ }
+ }
+
+ public void quitSynchronously() {
+ state = State.DONE;
+ CameraManager.get().stopPreview();
+ Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
+ quit.sendToTarget();
+ try {
+ decodeThread.join();
+ } 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.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
+ CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
+ activity.drawViewfinder();
+ }
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/decoding/DecodeFormatManager.java b/main/java/com/geogle/zxing/decoding/DecodeFormatManager.java
new file mode 100644
index 0000000..4107c8b
--- /dev/null
+++ b/main/java/com/geogle/zxing/decoding/DecodeFormatManager.java
@@ -0,0 +1,106 @@
+package com.geogle.zxing.decoding;/*
+ * 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.
+ */
+
+
+
+
+
+import android.content.Intent;
+import android.net.Uri;
+
+import com.google.zxing.BarcodeFormat;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Pattern;
+
+final class DecodeFormatManager {
+
+ private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+ static final Vector PRODUCT_FORMATS;
+ static final Vector ONE_D_FORMATS;
+ static final Vector QR_CODE_FORMATS;
+ static final Vector DATA_MATRIX_FORMATS;
+ static {
+ PRODUCT_FORMATS = new Vector(5);
+ PRODUCT_FORMATS.add(BarcodeFormat.UPC_A);
+ PRODUCT_FORMATS.add(BarcodeFormat.UPC_E);
+ PRODUCT_FORMATS.add(BarcodeFormat.EAN_13);
+ PRODUCT_FORMATS.add(BarcodeFormat.EAN_8);
+ ONE_D_FORMATS = new Vector(PRODUCT_FORMATS.size() + 4);
+ ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
+ ONE_D_FORMATS.add(BarcodeFormat.CODE_39);
+ ONE_D_FORMATS.add(BarcodeFormat.CODE_93);
+ ONE_D_FORMATS.add(BarcodeFormat.CODE_128);
+ ONE_D_FORMATS.add(BarcodeFormat.ITF);
+ QR_CODE_FORMATS = new Vector(1);
+ QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE);
+ DATA_MATRIX_FORMATS = new Vector(1);
+ DATA_MATRIX_FORMATS.add(BarcodeFormat.DATA_MATRIX);
+ }
+
+ private DecodeFormatManager() {}
+
+ static Vector parseDecodeFormats(Intent intent) {
+ List scanFormats = null;
+ String scanFormatsString = intent.getStringExtra(Intents.Scan.SCAN_FORMATS);
+ if (scanFormatsString != null) {
+ scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
+ }
+ return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
+ }
+
+ static Vector parseDecodeFormats(Uri inputUri) {
+ List formats = inputUri.getQueryParameters(Intents.Scan.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 Vector parseDecodeFormats(Iterable scanFormats,
+ String decodeMode) {
+ if (scanFormats != null) {
+ Vector formats = new Vector();
+ try {
+ for (String format : scanFormats) {
+ formats.add(BarcodeFormat.valueOf(format));
+ }
+ return formats;
+ } catch (IllegalArgumentException iae) {
+ // ignore it then
+ }
+ }
+ if (decodeMode != null) {
+ if (Intents.Scan.PRODUCT_MODE.equals(decodeMode)) {
+ return PRODUCT_FORMATS;
+ }
+ if (Intents.Scan.QR_CODE_MODE.equals(decodeMode)) {
+ return QR_CODE_FORMATS;
+ }
+ if (Intents.Scan.DATA_MATRIX_MODE.equals(decodeMode)) {
+ return DATA_MATRIX_FORMATS;
+ }
+ if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) {
+ return ONE_D_FORMATS;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/decoding/DecodeHandler.java b/main/java/com/geogle/zxing/decoding/DecodeHandler.java
new file mode 100644
index 0000000..58ba546
--- /dev/null
+++ b/main/java/com/geogle/zxing/decoding/DecodeHandler.java
@@ -0,0 +1,116 @@
+package com.geogle.zxing.decoding;/*
+ * 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.
+ */
+
+
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+
+import com.geogle.zxing.camera.PlanarYUVLuminanceSource;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+
+import com.geogle.zxing.camera.CameraManager;
+
+import com.google.zxing.common.HybridBinarizer;
+
+import java.util.Hashtable;
+
+import com.geogle.zxing.activity.CaptureActivity;
+import edu.hzuapps.androidlabs.R;
+
+
+final class DecodeHandler extends Handler {
+
+ private static final String TAG = DecodeHandler.class.getSimpleName();
+
+ private final CaptureActivity activity;
+ private final MultiFormatReader multiFormatReader;
+
+ DecodeHandler(CaptureActivity activity, Hashtable hints) {
+ multiFormatReader = new MultiFormatReader();
+ multiFormatReader.setHints(hints);
+ this.activity = activity;
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case R.id.decode:
+ //Log.d(TAG, "Got decode message");
+ decode((byte[]) message.obj, message.arg1, message.arg2);
+ break;
+ case R.id.quit:
+ 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;
+
+ //modify here
+ byte[] rotatedData = new byte[data.length];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ rotatedData[x * height + height - y - 1] = data[x + y * width];
+ }
+ int tmp = width; // Here we are swapping, that's the difference to #11
+ width = height;
+ height = tmp;
+
+ PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
+ BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+ try {
+ rawResult = multiFormatReader.decodeWithState(bitmap);
+ } catch (ReaderException re) {
+ // continue
+ } finally {
+ multiFormatReader.reset();
+ }
+
+ if (rawResult != null) {
+ long end = System.currentTimeMillis();
+ Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());
+ Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult);
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
+ message.setData(bundle);
+ //Log.d(TAG, "Sending decode succeeded message...");
+ message.sendToTarget();
+ } else {
+ Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);
+ message.sendToTarget();
+ }
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/decoding/DecodeThread.java b/main/java/com/geogle/zxing/decoding/DecodeThread.java
new file mode 100644
index 0000000..4ba3acf
--- /dev/null
+++ b/main/java/com/geogle/zxing/decoding/DecodeThread.java
@@ -0,0 +1,87 @@
+package com.geogle.zxing.decoding;/*
+ * 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.
+ */
+
+
+
+import android.os.Handler;
+import android.os.Looper;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.ResultPointCallback;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
+
+import com.geogle.zxing.activity.CaptureActivity;
+
+/**
+ * This thread does all the heavy lifting of decoding the images.
+ * �����߳�
+ */
+final class DecodeThread extends Thread {
+
+ public static final String BARCODE_BITMAP = "barcode_bitmap";
+ private final CaptureActivity activity;
+ private final Hashtable hints;
+ private Handler handler;
+ private final CountDownLatch handlerInitLatch;
+
+ DecodeThread(CaptureActivity activity,
+ Vector decodeFormats,
+ String characterSet,
+ ResultPointCallback resultPointCallback) {
+
+ this.activity = activity;
+ handlerInitLatch = new CountDownLatch(1);
+
+ hints = new Hashtable(3);
+
+ if (decodeFormats == null || decodeFormats.isEmpty()) {
+ decodeFormats = new Vector();
+ decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
+ decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
+ decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
+ }
+
+ hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
+
+ if (characterSet != null) {
+ hints.put(DecodeHintType.CHARACTER_SET, characterSet);
+ }
+
+ hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
+ }
+
+ 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();
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/decoding/FinishListener.java b/main/java/com/geogle/zxing/decoding/FinishListener.java
new file mode 100644
index 0000000..40dc709
--- /dev/null
+++ b/main/java/com/geogle/zxing/decoding/FinishListener.java
@@ -0,0 +1,47 @@
+/*
+ * 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.geogle.zxing.decoding;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+
+/**
+ * Simple listener used to exit the app in a few cases.
+ *
+ */
+public final class FinishListener
+ implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, Runnable {
+
+ private final Activity activityToFinish;
+
+ public FinishListener(Activity activityToFinish) {
+ this.activityToFinish = activityToFinish;
+ }
+
+ public void onCancel(DialogInterface dialogInterface) {
+ run();
+ }
+
+ public void onClick(DialogInterface dialogInterface, int i) {
+ run();
+ }
+
+ public void run() {
+ activityToFinish.finish();
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/decoding/InactivityTimer.java b/main/java/com/geogle/zxing/decoding/InactivityTimer.java
new file mode 100644
index 0000000..bfc8b20
--- /dev/null
+++ b/main/java/com/geogle/zxing/decoding/InactivityTimer.java
@@ -0,0 +1,71 @@
+/*
+ * 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.geogle.zxing.decoding;
+
+import android.app.Activity;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Finishes an activity after a period of inactivity.
+ */
+public final class InactivityTimer {
+
+ private static final int INACTIVITY_DELAY_SECONDS = 5 * 60;
+
+ private final ScheduledExecutorService inactivityTimer =
+ Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory());
+ private final Activity activity;
+ private ScheduledFuture> inactivityFuture = null;
+
+ public InactivityTimer(Activity activity) {
+ this.activity = activity;
+ onActivity();
+ }
+
+ public void onActivity() {
+ cancel();
+ inactivityFuture = inactivityTimer.schedule(new FinishListener(activity),
+ INACTIVITY_DELAY_SECONDS,
+ TimeUnit.SECONDS);
+ }
+
+ private void cancel() {
+ if (inactivityFuture != null) {
+ inactivityFuture.cancel(true);
+ inactivityFuture = null;
+ }
+ }
+
+ public void shutdown() {
+ cancel();
+ inactivityTimer.shutdown();
+ }
+
+ private static final class DaemonThreadFactory implements ThreadFactory {
+ public Thread newThread(Runnable runnable) {
+ Thread thread = new Thread(runnable);
+ thread.setDaemon(true);
+ return thread;
+ }
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/decoding/Intents.java b/main/java/com/geogle/zxing/decoding/Intents.java
new file mode 100644
index 0000000..afcdd47
--- /dev/null
+++ b/main/java/com/geogle/zxing/decoding/Intents.java
@@ -0,0 +1,190 @@
+/*
+ * 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.geogle.zxing.decoding;
+
+/**
+ * This class provides the constants to use when sending an Intent to Barcode Scanner.
+ * These strings are effectively API and cannot be changed.
+ */
+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 Scan.ACTION will decode all barcodes that we understand. However it
+ * may be useful to limit scanning to certain formats. Use Intent.putExtra(MODE, value) with
+ * one of the values below ({@link #PRODUCT_MODE}, {@link #ONE_D_MODE}, {@link #QR_CODE_MODE}).
+ * Optional.
+ *
+ * Setting this is effectively shorthnad for setting explicit formats with {@link #SCAN_FORMATS}.
+ * It is overridden by that setting.
+ */
+ public static final String MODE = "SCAN_MODE";
+
+ /**
+ * Comma-separated list of formats to scan for. The values must match the names of
+ * {@link com.google.zxing.BarcodeFormat}s, such as {@link com.google.zxing.BarcodeFormat#EAN_13}.
+ * Example: "EAN_13,EAN_8,QR_CODE"
+ *
+ * This overrides {@link #MODE}.
+ */
+ public static final String SCAN_FORMATS = "SCAN_FORMATS";
+
+ /**
+ * @see com.google.zxing.DecodeHintType#CHARACTER_SET
+ */
+ public static final String CHARACTER_SET = "CHARACTER_SET";
+
+ /**
+ * 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 (currently UPC, EAN, Code 39, and Code 128).
+ */
+ 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";
+
+ /**
+ * If a barcode is found, Barcodes returns RESULT_OK to onActivityResult() of the app which
+ * requested the scan via startSubActivity(). The barcodes contents can be retrieved with
+ * intent.getStringExtra(RESULT). If the user presses Back, the result code will be
+ * RESULT_CANCELED.
+ */
+ public static final String RESULT = "SCAN_RESULT";
+
+ /**
+ * Call intent.getStringExtra(RESULT_FORMAT) to determine which barcode format was found.
+ * See Contents.Format for possible values.
+ */
+ public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";
+
+ /**
+ * Setting this to false will not save scanned codes in the history.
+ */
+ public static final String SAVE_HISTORY = "SAVE_HISTORY";
+
+ private Scan() {
+ }
+ }
+
+ 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 Intent.putExtra(DATA, data) where data is either a String or a
+ * Bundle, depending on the type and format specified. Non-QR Code formats should
+ * 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
+ * Intent.putExtra(TYPE, type) with one of 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 Intent.putExtra(FORMAT, format), where
+ * format is one of Contents.Format.
+ */
+ public static final String FORMAT = "ENCODE_FORMAT";
+
+ 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() {
+ }
+ }
+}
diff --git a/main/java/com/geogle/zxing/decoding/RGBLuminanceSource.java b/main/java/com/geogle/zxing/decoding/RGBLuminanceSource.java
new file mode 100644
index 0000000..15dddb0
--- /dev/null
+++ b/main/java/com/geogle/zxing/decoding/RGBLuminanceSource.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2009 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.geogle.zxing.decoding;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import com.google.zxing.LuminanceSource;
+
+import java.io.FileNotFoundException;
+
+/**
+ * This class is used to help decode images from files which arrive as RGB data
+ * from Android bitmaps. It does not support cropping or rotation.
+ *
+ */
+public final class RGBLuminanceSource extends LuminanceSource {
+
+ private final byte[] luminances;
+
+ public RGBLuminanceSource(String path) throws FileNotFoundException {
+ this(loadBitmap(path));
+ }
+
+ public RGBLuminanceSource(Bitmap bitmap) {
+ super(bitmap.getWidth(), bitmap.getHeight());
+
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+
+ int[] pixels = new int[width * height];
+ bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+
+ // In order to measure pure decoding speed, we convert the entire image
+ // to a greyscale array
+ // up front, which is the same as the Y channel of the
+ // YUVLuminanceSource in the real app.
+ luminances = new byte[width * height];
+ for (int y = 0; y < height; y++) {
+ int offset = y * width;
+ for (int x = 0; x < width; x++) {
+ int pixel = pixels[offset + x];
+ int r = (pixel >> 16) & 0xff;
+ int g = (pixel >> 8) & 0xff;
+ int b = pixel & 0xff;
+ if (r == g && g == b) {
+ // Image is already greyscale, so pick any channel.
+ luminances[offset + x] = (byte) r;
+ } else {
+ // Calculate luminance cheaply, favoring green.
+ luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
+ }
+ }
+ }
+ }
+
+
+
+
+ @Override
+ public byte[] getRow(int y, byte[] row) {
+ if (y < 0 || y >= getHeight()) {
+ throw new IllegalArgumentException("Requested row is outside the image: " + y);
+ }
+ int width = getWidth();
+ if (row == null || row.length < width) {
+ row = new byte[width];
+ }
+
+ System.arraycopy(luminances, y * width, row, 0, width);
+ return row;
+ }
+
+ // Since this class does not support cropping, the underlying byte array
+ // already contains
+ // exactly what the caller is asking for, so give it to them without a copy.
+ @Override
+ public byte[] getMatrix() {
+ return luminances;
+ }
+
+ private static Bitmap loadBitmap(String path) throws FileNotFoundException {
+ Bitmap bitmap = BitmapFactory.decodeFile(path);
+ if (bitmap == null) {
+ throw new FileNotFoundException("Couldn't open " + path);
+ }
+ return bitmap;
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/encoding/EncodingHandler.java b/main/java/com/geogle/zxing/encoding/EncodingHandler.java
new file mode 100644
index 0000000..a0f1956
--- /dev/null
+++ b/main/java/com/geogle/zxing/encoding/EncodingHandler.java
@@ -0,0 +1,132 @@
+package com.geogle.zxing.encoding;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * @author Ryan Tang
+ *
+ */
+public final class EncodingHandler {
+ private static final int BLACK = 0xff000000;
+
+ public static Bitmap createQRCode(String str, int widthAndHeight) throws WriterException {
+ Hashtable hints = new Hashtable();
+ hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
+ BitMatrix matrix = new MultiFormatWriter().encode(str,
+ BarcodeFormat.QR_CODE, widthAndHeight, widthAndHeight);
+ int width = matrix.getWidth();
+ int height = matrix.getHeight();
+ int[] pixels = new int[width * height];
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (matrix.get(x, y)) {
+ pixels[y * width + x] = BLACK;
+ }
+ }
+ }
+ Bitmap bitmap = Bitmap.createBitmap(width, height,
+ Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return bitmap;
+ }
+
+ /**
+ * 创建二维码
+ *
+ * @param content content
+ * @param widthPix widthPix
+ * @param heightPix heightPix
+ * @param logoBm logoBm
+ * @return 二维码
+ */
+ public static Bitmap createQRCode(String content, int widthPix, int heightPix, Bitmap logoBm) {
+ try {
+ if (content == null || "".equals(content)) {
+ return null;
+ }
+ // 配置参数
+ Map hints = new HashMap<>();
+ hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
+ // 容错级别
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ // 图像数据转换,使用了矩阵转换
+ BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix,
+ heightPix, hints);
+ int[] pixels = new int[widthPix * heightPix];
+ // 下面这里按照二维码的算法,逐个生成二维码的图片,
+ // 两个for循环是图片横列扫描的结果
+ for (int y = 0; y < heightPix; y++) {
+ for (int x = 0; x < widthPix; x++) {
+ if (bitMatrix.get(x, y)) {
+ pixels[y * widthPix + x] = 0xff000000;
+ } else {
+ pixels[y * widthPix + x] = 0xffffffff;
+ }
+ }
+ }
+ // 生成二维码图片的格式,使用ARGB_8888
+ Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
+ if (logoBm != null) {
+ bitmap = addLogo(bitmap, logoBm);
+ }
+ //必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大!
+ return bitmap;
+ } catch (WriterException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 在二维码中间添加Logo图案
+ */
+ private static Bitmap addLogo(Bitmap src, Bitmap logo) {
+ if (src == null) {
+ return null;
+ }
+ if (logo == null) {
+ return src;
+ }
+ //获取图片的宽高
+ int srcWidth = src.getWidth();
+ int srcHeight = src.getHeight();
+ int logoWidth = logo.getWidth();
+ int logoHeight = logo.getHeight();
+ if (srcWidth == 0 || srcHeight == 0) {
+ return null;
+ }
+ if (logoWidth == 0 || logoHeight == 0) {
+ return src;
+ }
+ //logo大小为二维码整体大小的1/5
+ float scaleFactor = srcWidth * 1.0f / 5 / logoWidth;
+ Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
+ try {
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawBitmap(src, 0, 0, null);
+ canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2);
+ canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null);
+ canvas.save();
+ canvas.restore();
+ } catch (Exception e) {
+ bitmap = null;
+ e.getStackTrace();
+ }
+ return bitmap;
+ }
+}
diff --git a/main/java/com/geogle/zxing/view/ViewfinderResultPointCallback.java b/main/java/com/geogle/zxing/view/ViewfinderResultPointCallback.java
new file mode 100644
index 0000000..032ff63
--- /dev/null
+++ b/main/java/com/geogle/zxing/view/ViewfinderResultPointCallback.java
@@ -0,0 +1,33 @@
+/*
+ * 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.geogle.zxing.view;
+
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+
+public final class ViewfinderResultPointCallback implements ResultPointCallback {
+ private final ViewfinderView viewfinderView;
+
+ public ViewfinderResultPointCallback(ViewfinderView viewfinderView) {
+ this.viewfinderView = viewfinderView;
+ }
+
+ public void foundPossibleResultPoint(ResultPoint point) {
+ viewfinderView.addPossibleResultPoint(point);
+ }
+
+}
diff --git a/main/java/com/geogle/zxing/view/ViewfinderView.java b/main/java/com/geogle/zxing/view/ViewfinderView.java
new file mode 100644
index 0000000..173b6d4
--- /dev/null
+++ b/main/java/com/geogle/zxing/view/ViewfinderView.java
@@ -0,0 +1,286 @@
+/*
+ * 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.geogle.zxing.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
+import android.util.AttributeSet;
+import android.view.View;
+
+
+import com.google.zxing.ResultPoint;
+import com.geogle.zxing.camera.CameraManager;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import edu.hzuapps.androidlabs.R;
+
+
+/**
+ * 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 = 10L;
+ private static final int OPAQUE = 0xFF;
+ private static final int CORNER_RECT_WIDTH = 8; //扫描区边角的宽
+ private static final int CORNER_RECT_HEIGHT = 40; //扫描区边角的高
+ private static final int SCANNER_LINE_MOVE_DISTANCE = 5; //扫描线移动距离
+ private static final int SCANNER_LINE_HEIGHT = 10; //扫描线宽度
+
+ private final Paint paint;
+ private Bitmap resultBitmap;
+ //模糊区域颜色
+ private final int maskColor;
+ private final int resultColor;
+ //扫描区域边框颜色
+ private final int frameColor;
+ //扫描线颜色
+ private final int laserColor;
+ //四角颜色
+ private final int cornerColor;
+ //扫描点的颜色
+ private final int resultPointColor;
+ private int scannerAlpha;
+ //扫描区域提示文本
+ private final String labelText;
+ //扫描区域提示文本颜色
+ private final int labelTextColor;
+ private final float labelTextSize;
+
+ public static int scannerStart = 0;
+ public static int scannerEnd = 0;
+
+ private Collection possibleResultPoints;
+ private Collection lastPossibleResultPoints;
+
+ // This constructor is used when the class is built from an XML resource.
+ public ViewfinderView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ //初始化自定义属性信息
+ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView);
+ laserColor = array.getColor(R.styleable.ViewfinderView_laser_color, 0x00FF00);
+ cornerColor = array.getColor(R.styleable.ViewfinderView_corner_color, 0x00FF00);
+ frameColor = array.getColor(R.styleable.ViewfinderView_frame_color, 0xFFFFFF);
+ resultPointColor = array.getColor(R.styleable.ViewfinderView_result_point_color, 0xC0FFFF00);
+ maskColor = array.getColor(R.styleable.ViewfinderView_mask_color, 0x60000000);
+ resultColor = array.getColor(R.styleable.ViewfinderView_result_color, 0xB0000000);
+ labelTextColor = array.getColor(R.styleable.ViewfinderView_label_text_color, 0x90FFFFFF);
+ labelText = array.getString(R.styleable.ViewfinderView_label_text);
+ labelTextSize = array.getFloat(R.styleable.ViewfinderView_label_text_size, 36f);
+
+ // Initialize these once for performance rather than calling them every time in onDraw().
+ paint = new Paint();
+ paint.setAntiAlias(true);
+ scannerAlpha = 0;
+ possibleResultPoints = new HashSet(5);
+
+
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ Rect frame = CameraManager.get().getFramingRect();
+ if (frame == null) {
+ return;
+ }
+ if(scannerStart == 0 || scannerEnd == 0) {
+ scannerStart = frame.top;
+ scannerEnd = frame.bottom;
+ }
+
+ int width = canvas.getWidth();
+ int height = canvas.getHeight();
+ // Draw the exterior (i.e. outside the framing rect) darkened
+ drawExterior(canvas, frame, width, height);
+
+
+ if (resultBitmap != null) {
+ // Draw the opaque result bitmap over the scanning rectangle
+ paint.setAlpha(OPAQUE);
+ canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
+ } else {
+ // Draw a two pixel solid black border inside the framing rect
+ drawFrame(canvas, frame);
+ // 绘制边角
+ drawCorner(canvas, frame);
+ //绘制提示信息
+ drawTextInfo(canvas, frame);
+ // Draw a red "laser scanner" line through the middle to show decoding is active
+ drawLaserScanner(canvas, frame);
+
+ Collection currentPossible = possibleResultPoints;
+ Collection currentLast = lastPossibleResultPoints;
+ if (currentPossible.isEmpty()) {
+ lastPossibleResultPoints = null;
+ } else {
+ possibleResultPoints = new HashSet(5);
+ lastPossibleResultPoints = currentPossible;
+ paint.setAlpha(OPAQUE);
+ paint.setColor(resultPointColor);
+ for (ResultPoint point : currentPossible) {
+ canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint);
+ }
+ }
+ if (currentLast != null) {
+ paint.setAlpha(OPAQUE / 2);
+ paint.setColor(resultPointColor);
+ for (ResultPoint point : currentLast) {
+ canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint);
+ }
+ }
+
+ // Request another update at the animation interval, but only repaint the laser line,
+ // not the entire viewfinder mask.
+ //指定重绘区域,该方法会在子线程中执行
+ postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
+ }
+ }
+
+ //绘制文本
+ private void drawTextInfo(Canvas canvas, Rect frame) {
+ paint.setColor(labelTextColor);
+ paint.setTextSize(labelTextSize);
+ paint.setTextAlign(Paint.Align.CENTER);
+ canvas.drawText(labelText, frame.left + frame.width() / 2, frame.bottom + CORNER_RECT_HEIGHT * 1.5f, paint);
+ }
+
+
+ //绘制边角
+ private void drawCorner(Canvas canvas, Rect frame) {
+ paint.setColor(cornerColor);
+ //左上
+ canvas.drawRect(frame.left, frame.top, frame.left + CORNER_RECT_WIDTH, frame.top + CORNER_RECT_HEIGHT, paint);
+ canvas.drawRect(frame.left, frame.top, frame.left + CORNER_RECT_HEIGHT, frame.top + CORNER_RECT_WIDTH, paint);
+ //右上
+ canvas.drawRect(frame.right - CORNER_RECT_WIDTH, frame.top, frame.right, frame.top + CORNER_RECT_HEIGHT, paint);
+ canvas.drawRect(frame.right - CORNER_RECT_HEIGHT, frame.top, frame.right, frame.top + CORNER_RECT_WIDTH, paint);
+ //左下
+ canvas.drawRect(frame.left, frame.bottom - CORNER_RECT_WIDTH, frame.left + CORNER_RECT_HEIGHT, frame.bottom, paint);
+ canvas.drawRect(frame.left, frame.bottom - CORNER_RECT_HEIGHT, frame.left + CORNER_RECT_WIDTH, frame.bottom, paint);
+ //右下
+ canvas.drawRect(frame.right - CORNER_RECT_WIDTH, frame.bottom - CORNER_RECT_HEIGHT, frame.right, frame.bottom, paint);
+ canvas.drawRect(frame.right - CORNER_RECT_HEIGHT, frame.bottom - CORNER_RECT_WIDTH, frame.right, frame.bottom, paint);
+ }
+
+ //绘制扫描线
+ private void drawLaserScanner(Canvas canvas, Rect frame) {
+ 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);
+ //线性渐变
+ LinearGradient linearGradient = new LinearGradient(
+ frame.left, scannerStart,
+ frame.left, scannerStart + SCANNER_LINE_HEIGHT,
+ shadeColor(laserColor),
+ laserColor,
+ Shader.TileMode.MIRROR);
+
+ RadialGradient radialGradient = new RadialGradient(
+ (float)(frame.left + frame.width() / 2),
+ (float)(scannerStart + SCANNER_LINE_HEIGHT / 2),
+ 360f,
+ laserColor,
+ shadeColor(laserColor),
+ Shader.TileMode.MIRROR);
+
+ SweepGradient sweepGradient = new SweepGradient(
+ (float)(frame.left + frame.width() / 2),
+ (float)(scannerStart + SCANNER_LINE_HEIGHT),
+ shadeColor(laserColor),
+ laserColor);
+
+ ComposeShader composeShader = new ComposeShader(radialGradient, linearGradient, PorterDuff.Mode.ADD);
+
+ paint.setShader(radialGradient);
+ if(scannerStart <= scannerEnd) {
+ //矩形
+// canvas.drawRect(frame.left, scannerStart, frame.right, scannerStart + SCANNER_LINE_HEIGHT, paint);
+ //椭圆
+ RectF rectF = new RectF(frame.left + 2 * SCANNER_LINE_HEIGHT, scannerStart, frame.right - 2 * SCANNER_LINE_HEIGHT, scannerStart + SCANNER_LINE_HEIGHT);
+ canvas.drawOval(rectF, paint);
+ scannerStart += SCANNER_LINE_MOVE_DISTANCE;
+ } else {
+ scannerStart = frame.top;
+ }
+ paint.setShader(null);
+ }
+
+ //处理颜色模糊
+ public int shadeColor(int color) {
+ String hax = Integer.toHexString(color);
+ String result = "20"+hax.substring(2);
+ return Integer.valueOf(result, 16);
+ }
+
+ // 绘制扫描区边框 Draw a two pixel solid black border inside the framing rect
+ private void drawFrame(Canvas canvas, Rect frame) {
+ paint.setColor(frameColor);
+ canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint);
+ canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint);
+ canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint);
+ canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint);
+ }
+
+ // 绘制模糊区域 Draw the exterior (i.e. outside the framing rect) darkened
+ private void drawExterior(Canvas canvas, Rect frame, int width, int height) {
+ 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);
+ }
+
+ public void drawViewfinder() {
+ resultBitmap = null;
+ 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) {
+ possibleResultPoints.add(point);
+ }
+
+}
diff --git a/main/java/edu/hzuapps/androidlabs/MainActivity.java b/main/java/edu/hzuapps/androidlabs/MainActivity.java
new file mode 100644
index 0000000..fb58aa9
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/MainActivity.java
@@ -0,0 +1,14 @@
+package edu.hzuapps.androidlabs;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+
+public class MainActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ }
+}
\ No newline at end of file
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/CollectFragment.java b/main/java/edu/hzuapps/androidlabs/watchtv/CollectFragment.java
new file mode 100644
index 0000000..b308b4c
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/CollectFragment.java
@@ -0,0 +1,88 @@
+package edu.hzuapps.androidlabs.watchtv;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+
+import android.os.Parcelable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import edu.hzuapps.androidlabs.R;
+import edu.hzuapps.androidlabs.watchtv.room.Programs;
+
+
+public class CollectFragment extends Fragment {
+
+ ListView listView;
+ View rootView;
+ List dataList;
+ ArrayList tagList;
+
+ public CollectFragment() {
+ // Required empty public constructor
+ }
+
+ public static CollectFragment newInstances(List list, ArrayList tag){
+ CollectFragment collectFragment = new CollectFragment();
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList("data", (ArrayList extends Parcelable>) list);
+ bundle.putStringArrayList("tag",tag);;
+ collectFragment.setArguments(bundle);
+ return collectFragment;
+ }
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if(getArguments()!=null){
+ dataList = getArguments().getParcelableArrayList("data");
+ tagList = getArguments().getStringArrayList("tag");
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ if(rootView == null){
+ rootView = inflater.inflate(R.layout.fragment_collect, container, false);
+ }
+ // Inflate the layout for this fragment
+ initview();
+ return rootView;
+ }
+
+ private void initview() {
+ listView = rootView.findViewById(R.id.lv_home);
+ listView.setAdapter(new MyBaseAdapter(dataList,tagList,this.getContext()));
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ String str = dataList.get(position).getAddr();
+ ClipboardManager cm;
+ ClipData mClipData;
+ //获取剪贴板管理器:
+ cm = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
+ // 创建普通字符型ClipData
+ mClipData = ClipData.newPlainText("Label", str);
+ // 将ClipData内容放到系统剪贴板里。
+ cm.setPrimaryClip(mClipData);
+ Toast.makeText(getContext(), "视频地址已复制到剪切板",
+ Toast.LENGTH_SHORT).show();
+
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/HomeFragment.java b/main/java/edu/hzuapps/androidlabs/watchtv/HomeFragment.java
new file mode 100644
index 0000000..bde221c
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/HomeFragment.java
@@ -0,0 +1,108 @@
+package edu.hzuapps.androidlabs.watchtv;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+
+import androidx.annotation.RequiresApi;
+import androidx.fragment.app.Fragment;
+
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.MediaController;
+import android.widget.RelativeLayout;
+import android.widget.VideoView;
+
+import edu.hzuapps.androidlabs.R;
+import edu.hzuapps.androidlabs.watchtv.until.ToolUtils;
+
+import static android.content.Context.WINDOW_SERVICE;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Use the {@link HomeFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class HomeFragment extends Fragment implements View.OnClickListener {
+
+ View rootView;
+ protected Button play;
+ protected VideoView videoView;
+ protected EditText edittext;
+ protected Button newplayer;
+
+
+
+ public HomeFragment() {
+ // Required empty public constructor
+ }
+
+
+ public static HomeFragment newInstance() {
+ HomeFragment fragment = new HomeFragment();
+
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ if(rootView == null){
+ rootView = inflater.inflate(R.layout.fragment_home, container, false);
+ }
+ edittext = rootView.findViewById(R.id.et_home);
+ play = rootView.findViewById(R.id.bt_play);
+ videoView = rootView.findViewById(R.id.vv_home);
+ videoView.setMediaController(new MediaController(rootView.getContext()));
+ videoView.setVisibility(View.INVISIBLE);
+ play.setOnClickListener(this);
+ newplayer = rootView.findViewById(R.id.newplayer);
+ newplayer.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(getContext(),MovieActivity.class);
+ intent.setData(Uri.parse(edittext.getText().toString()));
+ startActivity(intent);
+ }
+ });
+
+
+
+
+ return rootView;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if(videoView!=null && videoView.isPlaying()){
+ videoView.stopPlayback();
+ }
+ videoView.setVideoURI(Uri.parse(edittext.getText().toString()));
+ videoView.setVisibility(View.VISIBLE);
+ videoView.start();
+ }
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/MovieActivity.java b/main/java/edu/hzuapps/androidlabs/watchtv/MovieActivity.java
new file mode 100644
index 0000000..e4c01ae
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/MovieActivity.java
@@ -0,0 +1,65 @@
+package edu.hzuapps.androidlabs.watchtv;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.MediaController;
+import android.widget.RelativeLayout;
+import android.widget.VideoView;
+
+import edu.hzuapps.androidlabs.R;
+
+public class MovieActivity extends AppCompatActivity {
+
+ VideoView videoView ;
+ EditText editText;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_movie);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.hide(); //隐藏标题栏
+ }
+
+ videoView = findViewById(R.id.videoview);
+ editText = findViewById(R.id.et_home);
+ videoView.setMediaController(new MediaController(this));
+ videoView.setVideoURI(getIntent().getData());
+ videoView.start();
+ }
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ videoView = findViewById(R.id.videoview);
+ if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,dip2px(this,235f));
+ params.addRule(RelativeLayout.CENTER_IN_PARENT);
+ videoView.setLayoutParams(params);
+ } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ videoView.setLayoutParams(new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT));
+ }
+ }
+
+ /**
+ * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
+ */
+ public static int dip2px(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+
+ }
+}
\ No newline at end of file
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/MyBaseAdapter.java b/main/java/edu/hzuapps/androidlabs/watchtv/MyBaseAdapter.java
new file mode 100644
index 0000000..01cae42
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/MyBaseAdapter.java
@@ -0,0 +1,56 @@
+package edu.hzuapps.androidlabs.watchtv;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.List;
+
+import edu.hzuapps.androidlabs.R;
+import edu.hzuapps.androidlabs.watchtv.room.Programs;
+
+public class MyBaseAdapter extends BaseAdapter {
+
+ private List data;
+ private List datatag;
+ private Context context;
+
+ public MyBaseAdapter(List data,List datatag, Context context) {
+ this.data = data;
+ this.datatag = datatag;
+ this.context = context;
+ }
+
+ @Override
+ public int getCount() {
+ return data.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ if(datatag.contains(data.get(position).getName())){
+ convertView = LayoutInflater.from(context).inflate(R.layout.list_item_tag, parent, false);
+ }else{
+ convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
+ }
+
+ }
+ TextView textview = convertView.findViewById(R.id.tv_item);
+ textview.setText(data.get(position).getName());
+ return convertView;
+ }
+}
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/MyFragmentAdapter.java b/main/java/edu/hzuapps/androidlabs/watchtv/MyFragmentAdapter.java
new file mode 100644
index 0000000..d105a7a
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/MyFragmentAdapter.java
@@ -0,0 +1,31 @@
+package edu.hzuapps.androidlabs.watchtv;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MyFragmentAdapter extends FragmentStateAdapter {
+
+ List list = new ArrayList<>();
+
+ public MyFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle,List flist) {
+ super(fragmentManager, lifecycle);
+ list = flist;
+ }
+
+ @NonNull
+ @Override
+ public Fragment createFragment(int position) {
+ return list.get(position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+}
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/MyVideoView.java b/main/java/edu/hzuapps/androidlabs/watchtv/MyVideoView.java
new file mode 100644
index 0000000..e3259c4
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/MyVideoView.java
@@ -0,0 +1,32 @@
+package edu.hzuapps.androidlabs.watchtv;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.VideoView;
+
+public class MyVideoView extends VideoView {
+ public MyVideoView(Context context) {
+ super(context);
+ }
+
+ public MyVideoView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public MyVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int width = getDefaultSize(0, widthMeasureSpec);
+ int height = getDefaultSize(0, heightMeasureSpec);
+ setMeasuredDimension(width,height);
+
+ }
+
+
+}
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/SelectActivity.java b/main/java/edu/hzuapps/androidlabs/watchtv/SelectActivity.java
new file mode 100644
index 0000000..9a1c9ee
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/SelectActivity.java
@@ -0,0 +1,35 @@
+package edu.hzuapps.androidlabs.watchtv;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import edu.hzuapps.androidlabs.R;
+
+public class SelectActivity extends AppCompatActivity implements View.OnClickListener {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_select);
+
+ Button back = findViewById(R.id.btn_back);
+ back.setOnClickListener(this);
+ Button search = findViewById(R.id.btn_search);
+ search.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()){
+ case R.id.btn_search:
+ break;
+ case R.id.btn_back:
+ finish();
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/UserFragment.java b/main/java/edu/hzuapps/androidlabs/watchtv/UserFragment.java
new file mode 100644
index 0000000..b6b9e0c
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/UserFragment.java
@@ -0,0 +1,59 @@
+package edu.hzuapps.androidlabs.watchtv;
+
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.bitmap.CircleCrop;
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
+import com.bumptech.glide.request.RequestOptions;
+
+import edu.hzuapps.androidlabs.R;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Use the {@link UserFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class UserFragment extends Fragment {
+
+ View rootview;
+
+
+ public UserFragment() {
+ // Required empty public constructor
+ }
+
+
+ public static UserFragment newInstance() {
+ UserFragment fragment = new UserFragment();
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ if(rootview == null){
+ rootview = inflater.inflate(R.layout.fragment_user, container, false);
+ }
+ ImageView imageView = rootview.findViewById(R.id.imageView);
+ Glide.with(this)
+ .load(R.drawable.myphoto)
+ .apply(RequestOptions.bitmapTransform(new RoundedCorners(180)))
+ .into(imageView);
+ // Inflate the layout for this fragment
+ return rootview;
+ }
+}
\ No newline at end of file
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/WatchTVActivity.java b/main/java/edu/hzuapps/androidlabs/watchtv/WatchTVActivity.java
new file mode 100644
index 0000000..7d1e95e
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/WatchTVActivity.java
@@ -0,0 +1,300 @@
+package edu.hzuapps.androidlabs.watchtv;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.viewpager2.widget.ViewPager2;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.MediaController;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+import android.widget.VideoView;
+
+import com.dommy.qrcode.util.Constant;
+import com.geogle.zxing.activity.CaptureActivity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import edu.hzuapps.androidlabs.R;
+import edu.hzuapps.androidlabs.watchtv.room.Programs;
+import edu.hzuapps.androidlabs.watchtv.room.manager.DBEngine;
+
+public class WatchTVActivity extends AppCompatActivity implements View.OnClickListener{
+
+ private static final String TAG = "zhu";
+ final WatchTVActivity thisActivity = this;
+ private List data = null;
+ private ArrayList datatag = null;
+ private Context thiscontext = this;
+
+ protected ViewPager2 viewPager;
+ protected LinearLayout lhome,lstar,lacc;
+ protected ImageView ivhome,ivstar,ivacc,ivcurr;
+ protected Button btnSearch,btnScanner,btnMore;
+
+
+
+
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_watch_t_v);
+
+ ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ 100);
+
+ DBEngine db = new DBEngine(thiscontext);
+
+ // dataInit();
+ // data = dataQuery();
+ dataInit();
+ initPage();
+ initMoudle();
+
+
+ }
+
+ private List dataQuery() {
+ List list = new ArrayList();
+// Resources resources = getResources();
+// String[] name = resources.getStringArray(R.array.name);
+// String[] url = resources.getStringArray(R.array.addr);
+// for (int i = 0; i < name.length; i++) {
+// Programs programs = new Programs();
+// programs.setName(name[i]);
+// programs.setAddr(url[i]);
+// programs.setId(i);
+// list.add(programs);
+// }
+// DBEngine db = new DBEngine(this);
+// List list = db.queryAllPrograms();
+
+
+ return list;
+ }
+
+ private void dataInit() {
+
+ data = new ArrayList<>();
+ datatag = new ArrayList();
+ Resources resources = getResources();
+ String[] url = resources.getStringArray(R.array.addr);
+ datatag.add("新闻");
+ Programs p = new Programs();
+ p.setName("新闻");
+ data.add(p);
+ int index = 0;
+ for (int i = 0; i < 5; i++) {
+ Programs programs = new Programs();
+ programs.setName("新闻"+(i+1));
+ programs.setAddr(url[index]);
+ programs.setId(index++);
+ data.add(programs);
+ }
+
+ datatag.add("娱乐");
+ p = new Programs();
+ p.setName("娱乐");
+ data.add(p);
+ for (int i = 0; i < 5; i++) {
+ Programs programs = new Programs();
+ programs.setName("娱乐"+(i+1));
+ programs.setAddr(url[index]);
+ programs.setId(index++);
+ data.add(programs);
+ }
+ datatag.add("自然");
+ p = new Programs();
+ p.setName("自然");
+ data.add(p);
+ for (int i = 0; i < 5; i++) {
+ Programs programs = new Programs();
+ programs.setName("自然"+(i+1));
+ programs.setAddr(url[index]);
+ programs.setId(index++);
+ data.add(programs);
+ }
+ }
+
+ private void initPage(){
+ viewPager = findViewById(R.id.vp_mid);
+ ArrayList fragments= new ArrayList<>();
+ fragments.add(HomeFragment.newInstance());
+ fragments.add(CollectFragment.newInstances(data,datatag));
+ fragments.add(UserFragment.newInstance());
+ viewPager.setAdapter(new MyFragmentAdapter(getSupportFragmentManager(),getLifecycle(),fragments));
+ viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ super.onPageScrolled(position, positionOffset, positionOffsetPixels);
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ super.onPageSelected(position);
+ changTab(position);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ super.onPageScrollStateChanged(state);
+ }
+ });
+ }
+
+
+
+ private void initMoudle(){
+ lhome = findViewById(R.id.part_home);
+ lstar = findViewById(R.id.part_collect);
+ lacc = findViewById(R.id.ll_friend);
+ ivhome = findViewById(R.id.iv_home);
+ ivstar = findViewById(R.id.iv_collect);
+ ivacc = findViewById(R.id.iv_user);
+ btnSearch = findViewById(R.id.btn_search);
+ btnScanner = findViewById(R.id.btn_scanner);
+ btnMore = findViewById(R.id.btn_more);
+
+ lhome.setOnClickListener(this);
+ lstar.setOnClickListener(this);
+ lacc.setOnClickListener(this);
+ btnSearch.setOnClickListener(this);
+ btnScanner.setOnClickListener(this);
+ btnMore.setOnClickListener(this);
+
+ ivcurr = ivhome;
+ ivhome.setSelected(true);
+ }
+
+ private void changTab(int position) {
+ ivcurr.setSelected(false);
+ switch (position){
+ case R.id.part_home:
+ viewPager.setCurrentItem(0);
+ case 0:
+ ivhome.setSelected(true);
+ ivcurr = ivhome;
+ break;
+ case R.id.part_collect:
+ viewPager.setCurrentItem(1);
+ case 1:
+ ivstar.setSelected(true);
+ ivcurr = ivstar;
+ break;
+ case R.id.ll_friend:
+ case R.id.btn_more:
+ viewPager.setCurrentItem(2);
+ case 2:
+ ivacc.setSelected(true);
+ ivcurr = ivacc;
+ break;
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()){
+ case R.id.btn_more:
+ case R.id.part_home:
+ case R.id.part_collect:
+ case R.id.ll_friend:
+ changTab(v.getId());
+ break;
+ case R.id.btn_search:
+ Intent intent = new Intent(thisActivity, SelectActivity.class);
+ thisActivity.startActivity(intent);
+ break;
+ case R.id.btn_scanner:
+ startQrCode();
+ break;
+ }
+
+ }
+
+ // 开始扫码
+ private void startQrCode() {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
+ Toast.makeText(WatchTVActivity.this, "请至权限中心打开本应用的相机访问权限", Toast.LENGTH_LONG).show();
+ }
+ // 申请权限
+ ActivityCompat.requestPermissions(WatchTVActivity.this, new String[]{Manifest.permission.CAMERA}, Constant.REQ_PERM_CAMERA);
+ return;
+ }
+ // 二维码扫码
+ Intent intent = new Intent(WatchTVActivity.this, CaptureActivity.class);
+ startActivityForResult(intent, Constant.REQ_QR_CODE);
+ }
+
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ String result = "";
+ //扫描结果回调
+ if (requestCode == Constant.REQ_QR_CODE && resultCode == RESULT_OK) {
+ if (data != null) {
+ Bundle bundle = data.getExtras();
+ if (bundle == null) {
+ return;
+ }
+ result = bundle.getString(Constant.INTENT_EXTRA_KEY_QR_SCAN);
+ }
+ }
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(result)); //splitflowurl为分流地址
+ if (!hasPreferredApplication(this,intent)){
+ intent.setClassName("com.android.browser", "com.android.browser.BrowserActivity");
+ }
+ startActivity(intent);
+
+ }
+
+ public static boolean hasPreferredApplication(Context context, Intent intent) {
+ PackageManager pm = context.getPackageManager();
+ ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ return !"android".equals(info.activityInfo.packageName);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ switch (requestCode) {
+ case Constant.REQ_PERM_CAMERA:
+ // 摄像头权限申请
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // 获得授权
+ startQrCode();
+ } else {
+ // 被禁止授权
+ Toast.makeText(WatchTVActivity.this, "请至权限中心打开本应用的相机访问权限", Toast.LENGTH_LONG).show();
+ }
+ break;
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/room/Programs.java b/main/java/edu/hzuapps/androidlabs/watchtv/room/Programs.java
new file mode 100644
index 0000000..e44181c
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/room/Programs.java
@@ -0,0 +1,93 @@
+package edu.hzuapps.androidlabs.watchtv.room;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.room.Entity;
+import androidx.room.Ignore;
+import androidx.room.PrimaryKey;
+
+@Entity
+public class Programs implements Parcelable {
+
+ @PrimaryKey(autoGenerate = true)
+ private int id;
+ private String name;
+ private String addr;
+
+ @Ignore
+ public Programs() {
+ }
+
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getAddr() {
+ return addr;
+ }
+
+ public void setAddr(String addr) {
+ this.addr = addr;
+ }
+
+ public Programs(int id, String name, String addr) {
+ this.id = id;
+ this.name = name;
+ this.addr = addr;
+ }
+
+ @Override
+ public String toString() {
+ return "Programs{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", addr='" + addr + '\'' +
+ '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
+ dest.writeString(name);
+ dest.writeString(addr);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Creator(){
+
+ @Override
+ public Programs createFromParcel(Parcel source) {
+ // TODO Auto-generated method stub
+ // 必须按成员变量声明的顺序读取数据,不然会出现获取数据出错
+ Programs p = new Programs();
+ p.setId(source.readInt());
+ p.setName(source.readString());
+ p.setAddr(source.readString());
+ return p;
+ }
+
+ @Override
+ public Programs[] newArray(int size) {
+ // TODO Auto-generated method stub
+ return new Programs[size];
+ }
+ };
+}
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/room/ProgramsDao.java b/main/java/edu/hzuapps/androidlabs/watchtv/room/ProgramsDao.java
new file mode 100644
index 0000000..296edb3
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/room/ProgramsDao.java
@@ -0,0 +1,16 @@
+package edu.hzuapps.androidlabs.watchtv.room;
+
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.Query;
+
+import java.util.List;
+
+@Dao
+public interface ProgramsDao {
+ @Insert
+ void insertPrograms(Programs... programs);
+
+ @Query("SELECT * FROM programs")
+ List queryAllPrograms();
+}
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/room/ProgramsDatabase.java b/main/java/edu/hzuapps/androidlabs/watchtv/room/ProgramsDatabase.java
new file mode 100644
index 0000000..ad39fce
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/room/ProgramsDatabase.java
@@ -0,0 +1,24 @@
+package edu.hzuapps.androidlabs.watchtv.room;
+
+import android.content.Context;
+
+import androidx.room.Database;
+import androidx.room.Room;
+import androidx.room.RoomDatabase;
+
+@Database(entities = {Programs.class},version = 1,exportSchema = false)
+public abstract class ProgramsDatabase extends RoomDatabase {
+
+ public abstract ProgramsDao getProgramsDao();
+
+ private static ProgramsDatabase INSTANCE;
+ public static synchronized ProgramsDatabase getINSTANCE(Context context){
+ if(INSTANCE == null){
+ INSTANCE = Room.databaseBuilder
+ (context.getApplicationContext(),ProgramsDatabase.class,"programe_database")
+ .allowMainThreadQueries()
+ .build();
+ }
+ return INSTANCE;
+ }
+}
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/room/manager/DBEngine.java b/main/java/edu/hzuapps/androidlabs/watchtv/room/manager/DBEngine.java
new file mode 100644
index 0000000..705cf35
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/room/manager/DBEngine.java
@@ -0,0 +1,49 @@
+package edu.hzuapps.androidlabs.watchtv.room.manager;
+
+import android.content.Context;
+
+import java.util.List;
+
+import edu.hzuapps.androidlabs.watchtv.room.Programs;
+import edu.hzuapps.androidlabs.watchtv.room.ProgramsDao;
+import edu.hzuapps.androidlabs.watchtv.room.ProgramsDatabase;
+
+public class DBEngine{
+
+ private ProgramsDao programsDao;
+ private List list;
+ private String teststr;
+
+
+ public DBEngine(Context context) {
+ ProgramsDatabase programsDatabase = ProgramsDatabase.getINSTANCE(context);
+ programsDao = programsDatabase.getProgramsDao();
+ }
+
+ public void insertPrograms(Programs... programs) {
+ programsDao.insertPrograms(programs);
+ }
+
+
+ public List queryAllPrograms() {
+ List list = programsDao.queryAllPrograms();
+ return list;
+ }
+
+// private static class InsertAsyncTask extends AsyncTask{
+//
+// private ProgramsDao dao;
+//
+// public InsertAsyncTask(ProgramsDao dao) {
+// this.dao = dao;
+// }
+//
+// @Override
+// protected Void doInBackground(Programs... programs) {
+// dao.insertPrograms(programs);
+// return null;
+// }
+// }
+
+
+}
diff --git a/main/java/edu/hzuapps/androidlabs/watchtv/until/ToolUtils.java b/main/java/edu/hzuapps/androidlabs/watchtv/until/ToolUtils.java
new file mode 100644
index 0000000..95a80bf
--- /dev/null
+++ b/main/java/edu/hzuapps/androidlabs/watchtv/until/ToolUtils.java
@@ -0,0 +1,13 @@
+package edu.hzuapps.androidlabs.watchtv.until;
+
+import androidx.fragment.app.FragmentActivity;
+
+public class ToolUtils {
+
+ public static int dip2px(FragmentActivity context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+
+
+}
diff --git a/main/res/drawable-v24/ic_launcher_foreground.xml b/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..cc14f03
--- /dev/null
+++ b/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/drawable-v24/logo.jpg b/main/res/drawable-v24/logo.jpg
new file mode 100644
index 0000000..077f572
Binary files /dev/null and b/main/res/drawable-v24/logo.jpg differ
diff --git a/main/res/drawable/abc_ic_menu_moreoverflow_mtrl_alpha.png b/main/res/drawable/abc_ic_menu_moreoverflow_mtrl_alpha.png
new file mode 100644
index 0000000..c695e31
Binary files /dev/null and b/main/res/drawable/abc_ic_menu_moreoverflow_mtrl_alpha.png differ
diff --git a/main/res/drawable/account_select.xml b/main/res/drawable/account_select.xml
new file mode 100644
index 0000000..5686596
--- /dev/null
+++ b/main/res/drawable/account_select.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/drawable/btn_back.png b/main/res/drawable/btn_back.png
new file mode 100644
index 0000000..8079ece
Binary files /dev/null and b/main/res/drawable/btn_back.png differ
diff --git a/main/res/drawable/edit_background.xml b/main/res/drawable/edit_background.xml
new file mode 100644
index 0000000..f7c141a
--- /dev/null
+++ b/main/res/drawable/edit_background.xml
@@ -0,0 +1,14 @@
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/drawable/flash_off.png b/main/res/drawable/flash_off.png
new file mode 100644
index 0000000..616f80a
Binary files /dev/null and b/main/res/drawable/flash_off.png differ
diff --git a/main/res/drawable/flash_on.png b/main/res/drawable/flash_on.png
new file mode 100644
index 0000000..d47ef8a
Binary files /dev/null and b/main/res/drawable/flash_on.png differ
diff --git a/main/res/drawable/house_select.xml b/main/res/drawable/house_select.xml
new file mode 100644
index 0000000..c96db56
--- /dev/null
+++ b/main/res/drawable/house_select.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/drawable/ic_baseline_account_circle_24.xml b/main/res/drawable/ic_baseline_account_circle_24.xml
new file mode 100644
index 0000000..89199eb
--- /dev/null
+++ b/main/res/drawable/ic_baseline_account_circle_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_account_circle_24_actvite.xml b/main/res/drawable/ic_baseline_account_circle_24_actvite.xml
new file mode 100644
index 0000000..4cac4ed
--- /dev/null
+++ b/main/res/drawable/ic_baseline_account_circle_24_actvite.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_attach_money_24.xml b/main/res/drawable/ic_baseline_attach_money_24.xml
new file mode 100644
index 0000000..fecf031
--- /dev/null
+++ b/main/res/drawable/ic_baseline_attach_money_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_chevron_left_24.xml b/main/res/drawable/ic_baseline_chevron_left_24.xml
new file mode 100644
index 0000000..09598f1
--- /dev/null
+++ b/main/res/drawable/ic_baseline_chevron_left_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_chevron_right_24.xml b/main/res/drawable/ic_baseline_chevron_right_24.xml
new file mode 100644
index 0000000..fd2878a
--- /dev/null
+++ b/main/res/drawable/ic_baseline_chevron_right_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_email_24.xml b/main/res/drawable/ic_baseline_email_24.xml
new file mode 100644
index 0000000..6943b4c
--- /dev/null
+++ b/main/res/drawable/ic_baseline_email_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_format_list_bulleted_24.xml b/main/res/drawable/ic_baseline_format_list_bulleted_24.xml
new file mode 100644
index 0000000..0b66281
--- /dev/null
+++ b/main/res/drawable/ic_baseline_format_list_bulleted_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_house_24.xml b/main/res/drawable/ic_baseline_house_24.xml
new file mode 100644
index 0000000..c199d14
--- /dev/null
+++ b/main/res/drawable/ic_baseline_house_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_house_24_active.xml b/main/res/drawable/ic_baseline_house_24_active.xml
new file mode 100644
index 0000000..900d6cc
--- /dev/null
+++ b/main/res/drawable/ic_baseline_house_24_active.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_live_tv_24.xml b/main/res/drawable/ic_baseline_live_tv_24.xml
new file mode 100644
index 0000000..4c6cd09
--- /dev/null
+++ b/main/res/drawable/ic_baseline_live_tv_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_local_fire_department_24.xml b/main/res/drawable/ic_baseline_local_fire_department_24.xml
new file mode 100644
index 0000000..adb2a4a
--- /dev/null
+++ b/main/res/drawable/ic_baseline_local_fire_department_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_more_horiz_24.xml b/main/res/drawable/ic_baseline_more_horiz_24.xml
new file mode 100644
index 0000000..6439bcc
--- /dev/null
+++ b/main/res/drawable/ic_baseline_more_horiz_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_qr_code_scanner_24.xml b/main/res/drawable/ic_baseline_qr_code_scanner_24.xml
new file mode 100644
index 0000000..597e8d7
--- /dev/null
+++ b/main/res/drawable/ic_baseline_qr_code_scanner_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_search_24.xml b/main/res/drawable/ic_baseline_search_24.xml
new file mode 100644
index 0000000..07b76d6
--- /dev/null
+++ b/main/res/drawable/ic_baseline_search_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_settings_24.xml b/main/res/drawable/ic_baseline_settings_24.xml
new file mode 100644
index 0000000..41a82ed
--- /dev/null
+++ b/main/res/drawable/ic_baseline_settings_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_share_24.xml b/main/res/drawable/ic_baseline_share_24.xml
new file mode 100644
index 0000000..2f13bb3
--- /dev/null
+++ b/main/res/drawable/ic_baseline_share_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_star_24.xml b/main/res/drawable/ic_baseline_star_24.xml
new file mode 100644
index 0000000..3383294
--- /dev/null
+++ b/main/res/drawable/ic_baseline_star_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_star_24_actvite.xml b/main/res/drawable/ic_baseline_star_24_actvite.xml
new file mode 100644
index 0000000..a474b21
--- /dev/null
+++ b/main/res/drawable/ic_baseline_star_24_actvite.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_baseline_update_24.xml b/main/res/drawable/ic_baseline_update_24.xml
new file mode 100644
index 0000000..3b92302
--- /dev/null
+++ b/main/res/drawable/ic_baseline_update_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/main/res/drawable/ic_launcher_background.xml b/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..a4f78de
--- /dev/null
+++ b/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/res/drawable/ic_switch_screen.xml b/main/res/drawable/ic_switch_screen.xml
new file mode 100644
index 0000000..ea2eee6
--- /dev/null
+++ b/main/res/drawable/ic_switch_screen.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/main/res/drawable/myphoto.jpg b/main/res/drawable/myphoto.jpg
new file mode 100644
index 0000000..47098b1
Binary files /dev/null and b/main/res/drawable/myphoto.jpg differ
diff --git a/main/res/drawable/search_shape.xml b/main/res/drawable/search_shape.xml
new file mode 100644
index 0000000..89c5bb1
--- /dev/null
+++ b/main/res/drawable/search_shape.xml
@@ -0,0 +1,15 @@
+
+
+ android:shape="rectangle">
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/drawable/star_select.xml b/main/res/drawable/star_select.xml
new file mode 100644
index 0000000..4ce6cf2
--- /dev/null
+++ b/main/res/drawable/star_select.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/drawable/thumbnail.xml b/main/res/drawable/thumbnail.xml
new file mode 100644
index 0000000..ea2eee6
--- /dev/null
+++ b/main/res/drawable/thumbnail.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_bottom.xml b/main/res/layout/activity_bottom.xml
new file mode 100644
index 0000000..49319ff
--- /dev/null
+++ b/main/res/layout/activity_bottom.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_main.xml b/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..ca0c0de
--- /dev/null
+++ b/main/res/layout/activity_main.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_mid.xml b/main/res/layout/activity_mid.xml
new file mode 100644
index 0000000..ddfaf4a
--- /dev/null
+++ b/main/res/layout/activity_mid.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_movie.xml b/main/res/layout/activity_movie.xml
new file mode 100644
index 0000000..2e8498b
--- /dev/null
+++ b/main/res/layout/activity_movie.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_relative_layout.xml b/main/res/layout/activity_relative_layout.xml
new file mode 100644
index 0000000..4fe216b
--- /dev/null
+++ b/main/res/layout/activity_relative_layout.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_scanner.xml b/main/res/layout/activity_scanner.xml
new file mode 100644
index 0000000..afdddc2
--- /dev/null
+++ b/main/res/layout/activity_scanner.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_select.xml b/main/res/layout/activity_select.xml
new file mode 100644
index 0000000..6673910
--- /dev/null
+++ b/main/res/layout/activity_select.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_soft1914080902251.xml b/main/res/layout/activity_soft1914080902251.xml
new file mode 100644
index 0000000..057b94e
--- /dev/null
+++ b/main/res/layout/activity_soft1914080902251.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_top.xml b/main/res/layout/activity_top.xml
new file mode 100644
index 0000000..3e5ac05
--- /dev/null
+++ b/main/res/layout/activity_top.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_view_by_java.xml b/main/res/layout/activity_view_by_java.xml
new file mode 100644
index 0000000..00fd495
--- /dev/null
+++ b/main/res/layout/activity_view_by_java.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/activity_watch_t_v.xml b/main/res/layout/activity_watch_t_v.xml
new file mode 100644
index 0000000..1505e7b
--- /dev/null
+++ b/main/res/layout/activity_watch_t_v.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/fragment_collect.xml b/main/res/layout/fragment_collect.xml
new file mode 100644
index 0000000..6d7a0af
--- /dev/null
+++ b/main/res/layout/fragment_collect.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/fragment_home.xml b/main/res/layout/fragment_home.xml
new file mode 100644
index 0000000..8635c0b
--- /dev/null
+++ b/main/res/layout/fragment_home.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/fragment_user.xml b/main/res/layout/fragment_user.xml
new file mode 100644
index 0000000..97bd667
--- /dev/null
+++ b/main/res/layout/fragment_user.xml
@@ -0,0 +1,218 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/list_item.xml b/main/res/layout/list_item.xml
new file mode 100644
index 0000000..835a413
--- /dev/null
+++ b/main/res/layout/list_item.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/layout/list_item_tag.xml b/main/res/layout/list_item_tag.xml
new file mode 100644
index 0000000..d7c2f72
--- /dev/null
+++ b/main/res/layout/list_item_tag.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/main/res/layout/toolbar_scanner.xml b/main/res/layout/toolbar_scanner.xml
new file mode 100644
index 0000000..d0b9ec3
--- /dev/null
+++ b/main/res/layout/toolbar_scanner.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/mipmap-anydpi-v26/ic_launcher.xml b/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..a26f6fb
--- /dev/null
+++ b/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..a26f6fb
--- /dev/null
+++ b/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/raw/beep.ogg b/main/res/raw/beep.ogg
new file mode 100644
index 0000000..cb1f7ba
Binary files /dev/null and b/main/res/raw/beep.ogg differ
diff --git a/main/res/values-night/themes.xml b/main/res/values-night/themes.xml
new file mode 100644
index 0000000..36b8ae2
--- /dev/null
+++ b/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/main/res/values/array.xml b/main/res/values/array.xml
new file mode 100644
index 0000000..e017513
--- /dev/null
+++ b/main/res/values/array.xml
@@ -0,0 +1,49 @@
+
+
+
+ - 视频1
+ - 视频2
+ - 视频3
+ - 视频4
+ - 视频5
+ - 视频6
+ - 视频7
+ - 视频8
+ - 视频9
+ - 视频10
+ - 视频11
+ - 视频12
+ - 视频13
+ - 视频14
+ - 视频15
+ - 视频16
+ - 视频17
+ - 视频18
+ - 视频19
+ - 视频20
+
+
+ - https://v-cdn.zjol.com.cn/280443.mp4
+ - https://v-cdn.zjol.com.cn/276982.mp4
+ - https://v-cdn.zjol.com.cn/276984.mp4
+ - https://v-cdn.zjol.com.cn/276985.mp4
+ - https://v-cdn.zjol.com.cn/276986.mp4
+ - https://v-cdn.zjol.com.cn/276987.mp4
+ - https://v-cdn.zjol.com.cn/276988.mp4
+ - https://v-cdn.zjol.com.cn/276989.mp4
+ - https://v-cdn.zjol.com.cn/276990.mp4
+ - https://v-cdn.zjol.com.cn/276991.mp4
+ - https://v-cdn.zjol.com.cn/276992.mp4
+ - https://v-cdn.zjol.com.cn/276993.mp4
+ - https://v-cdn.zjol.com.cn/276994.mp4
+ - https://v-cdn.zjol.com.cn/276996.mp4
+ - https://v-cdn.zjol.com.cn/276998.mp4
+ - https://v-cdn.zjol.com.cn/277000.mp4
+ - https://v-cdn.zjol.com.cn/277001.mp4
+ - https://v-cdn.zjol.com.cn/277002.mp4
+ - https://v-cdn.zjol.com.cn/277003.mp4
+ - https://v-cdn.zjol.com.cn/277004.mp4
+
+
+
+
\ No newline at end of file
diff --git a/main/res/values/attrs.xml b/main/res/values/attrs.xml
new file mode 100644
index 0000000..3c8736e
--- /dev/null
+++ b/main/res/values/attrs.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/values/colors.xml b/main/res/values/colors.xml
new file mode 100644
index 0000000..739432f
--- /dev/null
+++ b/main/res/values/colors.xml
@@ -0,0 +1,23 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #ededed
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+ #60000000
+ #B0000000
+ #90FFFFFF
+ #C0FFFF00
+ #0F0
+ #00FF00
+
+
\ No newline at end of file
diff --git a/main/res/values/ids.xml b/main/res/values/ids.xml
new file mode 100644
index 0000000..fb327cc
--- /dev/null
+++ b/main/res/values/ids.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml
new file mode 100644
index 0000000..79b7761
--- /dev/null
+++ b/main/res/values/strings.xml
@@ -0,0 +1,10 @@
+
+ AndroidLabs
+ is you
+ 123
+ reminder
+ done
+
+ Hello blank fragment
+ 1
+
\ No newline at end of file
diff --git a/main/res/values/themes.xml b/main/res/values/themes.xml
new file mode 100644
index 0000000..83e9e1e
--- /dev/null
+++ b/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file