(context,
218 | android.R.layout.simple_list_item_1, fileList) {
219 | @Override
220 | public View getView(int position, View convertView, ViewGroup parent) {
221 | TextView view = (TextView) super.getView(position, convertView,
222 | parent);
223 | if (fileList.get(position).equals(PARENT_FILE_NAME)) {
224 | view.setCompoundDrawablesWithIntrinsicBounds(
225 | upFolderDrawable, null, null, null);
226 | } else {
227 | File file = new File(currentFile, fileList.get(position));
228 | if (file.isDirectory()) {
229 | view.setCompoundDrawablesWithIntrinsicBounds(
230 | folderDrawable, null, null, null);
231 | } else {
232 | view.setCompoundDrawablesWithIntrinsicBounds(
233 | fileDrawable, null, null, null);
234 | }
235 | }
236 |
237 | return view;
238 | }
239 | };
240 | builder.setAdapter(adapter, this);
241 | }
242 |
243 | /**
244 | * @param context
245 | * @param builder
246 | * @author MD IMRAN HASAN HIRA ( imranhasanhira@gmail.com )
247 | */
248 | private void addPositiveButton(final Context context, Builder builder) {
249 | String positiveButtonText = "Ok";
250 |
251 | OnClickListener positiveButtonClickListener = new OnClickListener() {
252 |
253 | @Override
254 | public void onClick(DialogInterface dialog, int which) {
255 |
256 | if (dialogType == DialogType.SAVE_AS) {
257 | loadFilenameDialog("");
258 | } else {
259 | completeSelection("");
260 | }
261 |
262 | }
263 | };
264 | builder.setPositiveButton(positiveButtonText,
265 | positiveButtonClickListener);
266 | }
267 |
268 | /**
269 | * @param filename
270 | * @author MD IMRAN HASAN HIRA ( imranhasanhira@gmail.com )
271 | */
272 | protected void completeSelection(String filename) {
273 | currentDialog.dismiss();
274 | if (callback != null) {
275 | callback.onSelect(new File(currentFile, filename));
276 | callback = null;
277 | }
278 | }
279 |
280 | /**
281 | * @param filename
282 | * @author MD IMRAN HASAN HIRA ( imranhasanhira@gmail.com )
283 | */
284 | protected void loadFilenameDialog(String filename) {
285 | if (currentDialog != null) {
286 | currentDialog.dismiss();
287 | }
288 |
289 | Builder builder = new Builder(context);
290 | builder.setTitle("Please type a filename");
291 |
292 | final EditText et = new EditText(context);
293 | et.setHint("filename");
294 | et.setText(filename);
295 | builder.setView(et);
296 |
297 | builder.setPositiveButton("Go", new OnClickListener() {
298 |
299 | @Override
300 | public void onClick(DialogInterface dialog, int which) {
301 | String filename = et.getText().toString();
302 | if (filename == null || filename.length() <= 0) {
303 | Toast.makeText(context, "Invalid name", Toast.LENGTH_SHORT)
304 | .show();
305 | } else {
306 | if (new File(currentFile, filename).exists()) {
307 | loadDeleteExistingFileDialog(filename);
308 | } else {
309 | completeSelection(filename);
310 | }
311 | }
312 | }
313 | });
314 |
315 | builder.setOnCancelListener(new OnCancelListener() {
316 |
317 | @Override
318 | public void onCancel(DialogInterface arg0) {
319 | loadFilelist();
320 | }
321 | });
322 | currentDialog = builder.show();
323 | }
324 |
325 | /**
326 | * @param newFileName
327 | * @author MD IMRAN HASAN HIRA ( imranhasanhira@gmail.com )
328 | */
329 | protected void loadDeleteExistingFileDialog(final String newFileName) {
330 | Builder builder = new Builder(context);
331 | builder.setTitle("File already exist. Delete existing ?");
332 | builder.setPositiveButton("Yes", new OnClickListener() {
333 |
334 | @Override
335 | public void onClick(DialogInterface arg0, int arg1) {
336 | File selectedFile = new File(currentFile, newFileName);
337 | selectedFile.delete();
338 | try {
339 | selectedFile.createNewFile();
340 | completeSelection(newFileName);
341 | } catch (IOException e) {
342 | e.printStackTrace();
343 | loadFilelist();
344 | }
345 | }
346 | });
347 |
348 | builder.setNegativeButton("No", new OnClickListener() {
349 |
350 | @Override
351 | public void onClick(DialogInterface dialog, int which) {
352 | loadFilelist();
353 | }
354 | });
355 | builder.setOnCancelListener(new OnCancelListener() {
356 |
357 | @Override
358 | public void onCancel(DialogInterface arg0) {
359 | loadFilelist();
360 | }
361 | });
362 | builder.show();
363 |
364 | }
365 |
366 | /*
367 | * @author MD IMRAN HASAN HIRA ( imranhasanhira@gmail.com )(non-Javadoc)
368 | *
369 | * @see
370 | * android.content.DialogInterface.OnClickListener#onClick(android.content
371 | * .DialogInterface, int)
372 | */
373 | @Override
374 | public void onClick(DialogInterface dialog, int which) {
375 | if (fileList.get(which).equals(PARENT_FILE_NAME)) {
376 | currentFile = currentFile.getParentFile();
377 | loadFilelist();
378 | } else {
379 | File selectedFile = new File(currentFile, fileList.get(which));
380 | if (selectedFile.isDirectory()) {
381 | currentFile = selectedFile;
382 | loadFilelist();
383 | } else {
384 | if (dialogType == DialogType.SAVE_AS) {
385 | loadFilenameDialog(selectedFile.getName());
386 | } else {
387 | completeSelection(selectedFile.getName());
388 | }
389 | }
390 | }
391 | }
392 |
393 | /**
394 | *
395 | * @author MD IMRAN HASAN HIRA ( imranhasanhira@gmail.com )
396 | */
397 | class ImageLib {
398 | public static final String FOLDER = "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";
399 | public static final String FILE_1 = "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";
400 | public static final String FILE_2 = "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";
401 | public static final String UP_FOLDER_1 = "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";
402 |
403 | BitmapDrawable get(String base64Enc) {
404 | byte[] decodedString = Base64.decode(base64Enc, Base64.DEFAULT);
405 | Bitmap bitmap = BitmapFactory.decodeByteArray(decodedString, 0,
406 | decodedString.length);
407 | return new BitmapDrawable(context.getResources(), bitmap);
408 | }
409 | }
410 |
411 | }
412 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ttv/livedemo/LivenessDetectorProcesser.java:
--------------------------------------------------------------------------------
1 | package com.ttv.livedemo;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.os.Handler;
6 | import android.os.Looper;
7 |
8 | import com.ttv.face.FaceEngine;
9 | import com.ttv.face.FaceResult;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | import io.fotoapparat.parameter.Size;
15 | import io.fotoapparat.preview.Frame;
16 | import io.fotoapparat.preview.FrameProcessor;
17 |
18 | /**
19 | * {@link FrameProcessor} which detects faces on camera frames.
20 | *
21 | * Use {@link #with(Context)} to create a new instance.
22 | */
23 | public class LivenessDetectorProcesser implements FrameProcessor {
24 |
25 | private static Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
26 |
27 | private final OnFacesDetectedListener listener;
28 | private FaceEngine faceEngine;
29 |
30 | private LivenessDetectorProcesser(Builder builder) {
31 | faceEngine = FaceEngine.getInstance();
32 | listener = builder.listener;
33 | }
34 |
35 | public static Builder with(Context context) {
36 | return new Builder(context);
37 | }
38 |
39 | @Override
40 | public void processFrame(Frame frame) {
41 | Bitmap bitmap = faceEngine.yuvToBitmap(frame.image, frame.size.width, frame.size.height, frame.size.width, frame.size.height, frame.rotation, true);
42 | List faceInfoList = faceEngine.detectFace(bitmap);
43 | MAIN_THREAD_HANDLER.post(new Runnable() {
44 | @Override
45 | public void run() {
46 | listener.onFacesDetected(faceInfoList, frame.size);
47 | }
48 | });
49 | }
50 |
51 | /**
52 | * Notified when faces are detected.
53 | */
54 | public interface OnFacesDetectedListener {
55 |
56 | /**
57 | * Null-object for {@link OnFacesDetectedListener}.
58 | */
59 | OnFacesDetectedListener NULL = new OnFacesDetectedListener() {
60 | @Override
61 | public void onFacesDetected(List faces, Size frameSize) {
62 | // Do nothing
63 | }
64 | };
65 |
66 | /**
67 | * Called when faces are detected. Always called on the main thread.
68 | *
69 | * @param faces detected faces. If no faces were detected - an empty list.
70 | */
71 | void onFacesDetected(List faces, Size frameSize);
72 |
73 | }
74 |
75 | /**
76 | * Builder for {@link LivenessDetectorProcesser}.
77 | */
78 | public static class Builder {
79 |
80 | private final Context context;
81 | private OnFacesDetectedListener listener = OnFacesDetectedListener.NULL;
82 |
83 | private Builder(Context context) {
84 | this.context = context;
85 | }
86 |
87 | /**
88 | * @param listener which will be notified when faces are detected.
89 | */
90 | public Builder listener(OnFacesDetectedListener listener) {
91 | this.listener = listener != null
92 | ? listener
93 | : OnFacesDetectedListener.NULL;
94 |
95 | return this;
96 | }
97 |
98 | public LivenessDetectorProcesser build() {
99 | return new LivenessDetectorProcesser(this);
100 | }
101 |
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ttv/livedemo/PermissionsDelegate.java:
--------------------------------------------------------------------------------
1 | package com.ttv.livedemo;
2 |
3 | import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
4 | import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
5 | import static android.os.Build.VERSION.SDK_INT;
6 |
7 | import android.Manifest;
8 | import android.app.Activity;
9 | import android.content.Intent;
10 | import android.content.pm.PackageManager;
11 | import android.net.Uri;
12 | import android.os.Build;
13 | import android.os.Environment;
14 | import android.provider.Settings;
15 | import android.util.Log;
16 |
17 | import androidx.core.app.ActivityCompat;
18 | import androidx.core.content.ContextCompat;
19 |
20 | public class PermissionsDelegate {
21 |
22 | private static final int REQUEST_CODE = 10;
23 | private final Activity activity;
24 |
25 | public PermissionsDelegate(Activity activity) {
26 | this.activity = activity;
27 | }
28 |
29 | public boolean hasCameraPersmission() {
30 | return (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED);
31 | }
32 |
33 | public boolean hasPermissions() {
34 | int write = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE);
35 | int read = ContextCompat.checkSelfPermission(activity, READ_EXTERNAL_STORAGE);
36 | int camera = ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA);
37 | return (write == PackageManager.PERMISSION_GRANTED &&
38 | read == PackageManager.PERMISSION_GRANTED &&
39 | camera == PackageManager.PERMISSION_GRANTED);
40 | }
41 |
42 | public void requestPermissions() {
43 | if(ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
44 | ContextCompat.checkSelfPermission(activity, READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
45 | ActivityCompat.requestPermissions(activity, new String[]{WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE}, REQUEST_CODE);
46 | return;
47 | }
48 |
49 | if(ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
50 | ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, REQUEST_CODE);
51 | return;
52 | }
53 | }
54 |
55 | public boolean resultGranted(int requestCode,
56 | String[] permissions,
57 | int[] grantResults) {
58 |
59 | if (requestCode != REQUEST_CODE) {
60 | return false;
61 | }
62 |
63 | if(hasPermissions())
64 | return true;
65 |
66 | requestPermissions();
67 | return false;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/QrCodeActivity.java:
--------------------------------------------------------------------------------
1 | package qrcode;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.content.pm.PackageManager;
8 | import android.database.Cursor;
9 | import android.graphics.SurfaceTexture;
10 | import android.media.AudioManager;
11 | import android.media.MediaPlayer;
12 | import android.net.Uri;
13 | import android.os.Bundle;
14 | import android.os.Handler;
15 | import android.os.Message;
16 | import android.text.TextUtils;
17 | import android.view.TextureView;
18 | import android.view.View;
19 | import android.view.View.OnClickListener;
20 | import android.view.Window;
21 | import android.widget.Toast;
22 |
23 | import com.ttv.livedemo.R;
24 | import com.google.zxing.Result;
25 |
26 | import java.io.IOException;
27 | import java.lang.ref.WeakReference;
28 | import java.util.concurrent.Executor;
29 | import java.util.concurrent.Executors;
30 |
31 | import qrcode.camera.CameraManager;
32 | import qrcode.decode.CaptureActivityHandler;
33 | import qrcode.decode.DecodeImageCallback;
34 | import qrcode.decode.DecodeImageThread;
35 | import qrcode.decode.DecodeManager;
36 | import qrcode.decode.InactivityTimer;
37 | import qrcode.view.QrCodeFinderView;
38 |
39 | /**
40 | * Created by xingli on 12/26/15.
41 | *
42 | * 二维码扫描类。
43 | */
44 | public class QrCodeActivity extends Activity implements TextureView.SurfaceTextureListener, OnClickListener {
45 |
46 | private static final int REQUEST_SYSTEM_PICTURE = 0;
47 | private static final int REQUEST_PICTURE = 1;
48 | public static final int MSG_DECODE_SUCCEED = 1;
49 | public static final int MSG_DECODE_FAIL = 2;
50 | private CaptureActivityHandler mCaptureActivityHandler;
51 | private boolean mHasSurface;
52 | private boolean mPermissionOk;
53 | private InactivityTimer mInactivityTimer;
54 | private QrCodeFinderView mQrCodeFinderView;
55 | private TextureView mSurfaceView;
56 | private final DecodeManager mDecodeManager = new DecodeManager();
57 | /**
58 | * 声音和振动相关参数
59 | */
60 | private static final float BEEP_VOLUME = 0.10f;
61 | private static final long VIBRATE_DURATION = 200L;
62 | private MediaPlayer mMediaPlayer;
63 | private boolean mPlayBeep;
64 | private boolean mVibrate;
65 | private Executor mQrCodeExecutor;
66 | private Handler mHandler;
67 |
68 | private static Intent createIntent(Context context) {
69 | Intent i = new Intent(context, QrCodeActivity.class);
70 | return i;
71 | }
72 |
73 | public static void launch(Context context) {
74 | Intent i = createIntent(context);
75 | context.startActivity(i);
76 | }
77 |
78 | @Override
79 | public void onCreate(Bundle savedInstanceState) {
80 | requestWindowFeature(Window.FEATURE_NO_TITLE);
81 | super.onCreate(savedInstanceState);
82 | setContentView(R.layout.activity_qr_code);
83 | initView();
84 | initData();
85 | }
86 |
87 | private void checkPermission() {
88 | boolean hasHardware = checkCameraHardWare(this);
89 | if (hasHardware) {
90 | if (!hasCameraPermission()) {
91 | findViewById(R.id.qr_code_view_background).setVisibility(View.VISIBLE);
92 | mQrCodeFinderView.setVisibility(View.GONE);
93 | mPermissionOk = false;
94 | } else {
95 | mPermissionOk = true;
96 | }
97 | } else {
98 | mPermissionOk = false;
99 | finish();
100 | }
101 | }
102 |
103 | private void initView() {
104 | mQrCodeFinderView = (QrCodeFinderView) findViewById(R.id.qr_code_view_finder);
105 | mSurfaceView = (TextureView) findViewById(R.id.qr_code_preview_view);
106 | mSurfaceView.setSurfaceTextureListener(this);
107 | mSurfaceView.setOnClickListener(this);
108 |
109 | mHasSurface = false;
110 | }
111 |
112 | private void initData() {
113 | CameraManager.init(this);
114 | mInactivityTimer = new InactivityTimer(QrCodeActivity.this);
115 | mQrCodeExecutor = Executors.newSingleThreadExecutor();
116 | mHandler = new WeakHandler(this);
117 | }
118 |
119 | private boolean hasCameraPermission() {
120 | PackageManager pm = getPackageManager();
121 | return PackageManager.PERMISSION_GRANTED == pm.checkPermission("android.permission.CAMERA", getPackageName());
122 | }
123 |
124 | @Override
125 | protected void onResume() {
126 | super.onResume();
127 | checkPermission();
128 | if (!mPermissionOk) {
129 | mDecodeManager.showPermissionDeniedDialog(this);
130 | return;
131 | }
132 | SurfaceTexture surfaceHolder = mSurfaceView.getSurfaceTexture();
133 | if (mHasSurface) {
134 | initCamera(surfaceHolder);
135 | }
136 |
137 | mPlayBeep = true;
138 | AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
139 | if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
140 | mPlayBeep = false;
141 | }
142 | initBeepSound();
143 | mVibrate = true;
144 | }
145 |
146 | @Override
147 | protected void onPause() {
148 | super.onPause();
149 | if (mCaptureActivityHandler != null) {
150 | mCaptureActivityHandler.quitSynchronously();
151 | mCaptureActivityHandler = null;
152 | }
153 | CameraManager.get().closeDriver();
154 | }
155 |
156 | @Override
157 | protected void onDestroy() {
158 | if (null != mInactivityTimer) {
159 | mInactivityTimer.shutdown();
160 | }
161 | super.onDestroy();
162 | }
163 |
164 | @Override
165 | public void onClick(View v) {
166 | finish();
167 | }
168 |
169 | /**
170 | * Handler scan result
171 | *
172 | * @param result
173 | */
174 | public void handleDecode(Result result) {
175 | mInactivityTimer.onActivity();
176 | playBeepSoundAndVibrate();
177 | if (null == result) {
178 | mDecodeManager.showCouldNotReadQrCodeFromScanner(this, new DecodeManager.OnRefreshCameraListener() {
179 | @Override
180 | public void refresh() {
181 |
182 | restartPreview();
183 | }
184 | });
185 | } else {
186 | String resultString = result.getText();
187 | handleResult(resultString);
188 | }
189 | }
190 |
191 | private void initCamera(SurfaceTexture surfaceHolder) {
192 | try {
193 | CameraManager.get().openDriver(surfaceHolder);
194 | } catch (IOException e) {
195 | // 基本不会出现相机不存在的情况
196 | Toast.makeText(this, getString(R.string.qr_code_camera_not_found), Toast.LENGTH_SHORT).show();
197 | finish();
198 | return;
199 | } catch (RuntimeException re) {
200 | re.printStackTrace();
201 | mDecodeManager.showPermissionDeniedDialog(this);
202 | return;
203 | }
204 | mQrCodeFinderView.setVisibility(View.VISIBLE);
205 | mSurfaceView.setVisibility(View.VISIBLE);
206 | findViewById(R.id.qr_code_view_background).setVisibility(View.GONE);
207 | if (mCaptureActivityHandler == null) {
208 | mCaptureActivityHandler = new CaptureActivityHandler(this);
209 | }
210 | }
211 |
212 | private void restartPreview() {
213 | if (null != mCaptureActivityHandler) {
214 | mCaptureActivityHandler.restartPreviewAndDecode();
215 | }
216 | }
217 |
218 | /* 检测相机是否存在 */
219 | private boolean checkCameraHardWare(Context context) {
220 | PackageManager packageManager = context.getPackageManager();
221 | return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA);
222 | }
223 |
224 | @Override
225 | public void onSurfaceTextureAvailable(SurfaceTexture arg0, int arg1, int arg2) {
226 | if (!mHasSurface) {
227 | mHasSurface = true;
228 | initCamera(arg0);
229 | }
230 | }
231 |
232 | @Override
233 | public boolean onSurfaceTextureDestroyed(SurfaceTexture arg0) {
234 | mHasSurface = false;
235 | return true;
236 | }
237 |
238 | @Override
239 | public void onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1, int arg2) {
240 | }
241 |
242 | @Override
243 | public void onSurfaceTextureUpdated(SurfaceTexture arg0) {
244 | // Matrix transform = new Matrix();
245 | // transform.setScale(-1, 1, mSurfaceView.getWidth() / 2, 0);
246 | // mSurfaceView.setTransform(transform);
247 | }
248 |
249 | public Handler getCaptureActivityHandler() {
250 | return mCaptureActivityHandler;
251 | }
252 |
253 | private void initBeepSound() {
254 | if (mPlayBeep && mMediaPlayer == null) {
255 | // The volume on STREAM_SYSTEM is not adjustable, and users found it too loud,
256 | // so we now play on the music stream.
257 | // setVolumeControlStream(AudioManager.STREAM_MUSIC);
258 | // mMediaPlayer = new MediaPlayer();
259 | // mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
260 | // mMediaPlayer.setOnCompletionListener(mBeepListener);
261 | //
262 | // AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.beep);
263 | // try {
264 | // mMediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
265 | // file.close();
266 | // mMediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
267 | // mMediaPlayer.prepare();
268 | // } catch (IOException e) {
269 | // mMediaPlayer = null;
270 | // }
271 | }
272 | }
273 |
274 | private void playBeepSoundAndVibrate() {
275 | if (mPlayBeep && mMediaPlayer != null) {
276 | mMediaPlayer.start();
277 | }
278 | // if (mVibrate) {
279 | // Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
280 | // vibrator.vibrate(VIBRATE_DURATION);
281 | // }
282 | }
283 |
284 | /**
285 | * When the beep has finished playing, rewind to queue up another one.
286 | */
287 | private final MediaPlayer.OnCompletionListener mBeepListener = new MediaPlayer.OnCompletionListener() {
288 | public void onCompletion(MediaPlayer mediaPlayer) {
289 | mediaPlayer.seekTo(0);
290 | }
291 | };
292 |
293 | private void handleResult(String resultString) {
294 | if (TextUtils.isEmpty(resultString)) {
295 | mDecodeManager.showCouldNotReadQrCodeFromScanner(this, new DecodeManager.OnRefreshCameraListener() {
296 | @Override
297 | public void refresh() {
298 | restartPreview();
299 | }
300 | });
301 | } else {
302 | // mDecodeManager.showResultDialog(this, resultString, new DialogInterface.OnClickListener() {
303 | // @Override
304 | // public void onClick(DialogInterface dialog, int which) {
305 | // dialog.dismiss();
306 | // restartPreview();
307 | // }
308 | // });
309 | Intent intent = new Intent();
310 | intent.putExtra("Result", resultString);
311 | setResult(RESULT_OK, intent);
312 | finish();
313 | }
314 | }
315 |
316 | @Override
317 | protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
318 | if (resultCode != RESULT_OK) {
319 | return;
320 | }
321 | switch (requestCode) {
322 | case REQUEST_PICTURE:
323 | finish();
324 | break;
325 | case REQUEST_SYSTEM_PICTURE:
326 | Uri uri = data.getData();
327 | Cursor cursor = getContentResolver().query(uri, null, null, null, null);
328 | if (null != cursor) {
329 | cursor.moveToFirst();
330 | String imgPath = cursor.getString(1); // 图片文件路径
331 | cursor.close();
332 | if (null != mQrCodeExecutor && !TextUtils.isEmpty(imgPath)) {
333 | mQrCodeExecutor.execute(new DecodeImageThread(imgPath, mDecodeImageCallback));
334 | }
335 | }
336 | break;
337 | }
338 | }
339 |
340 | private DecodeImageCallback mDecodeImageCallback = new DecodeImageCallback() {
341 | @Override
342 | public void decodeSucceed(Result result) {
343 | mHandler.obtainMessage(MSG_DECODE_SUCCEED, result).sendToTarget();
344 | }
345 |
346 | @Override
347 | public void decodeFail(int type, String reason) {
348 | mHandler.sendEmptyMessage(MSG_DECODE_FAIL);
349 | }
350 | };
351 |
352 | private static class WeakHandler extends Handler {
353 | private WeakReference mWeakQrCodeActivity;
354 | private DecodeManager mDecodeManager = new DecodeManager();
355 |
356 | public WeakHandler(QrCodeActivity imagePickerActivity) {
357 | super();
358 | this.mWeakQrCodeActivity = new WeakReference<>(imagePickerActivity);
359 | }
360 |
361 | @Override
362 | public void handleMessage(Message msg) {
363 | QrCodeActivity qrCodeActivity = mWeakQrCodeActivity.get();
364 | switch (msg.what) {
365 | case MSG_DECODE_SUCCEED:
366 | Result result = (Result) msg.obj;
367 | if (null == result) {
368 | mDecodeManager.showCouldNotReadQrCodeFromPicture(qrCodeActivity);
369 | } else {
370 | String resultString = result.getText();
371 | handleResult(resultString);
372 | }
373 | break;
374 | case MSG_DECODE_FAIL:
375 | mDecodeManager.showCouldNotReadQrCodeFromPicture(qrCodeActivity);
376 | break;
377 | }
378 | super.handleMessage(msg);
379 | }
380 |
381 | private void handleResult(String resultString) {
382 | QrCodeActivity imagePickerActivity = mWeakQrCodeActivity.get();
383 | mDecodeManager.showResultDialog(imagePickerActivity, resultString, new DialogInterface.OnClickListener() {
384 | @Override
385 | public void onClick(DialogInterface dialog, int which) {
386 | dialog.dismiss();
387 | }
388 | });
389 | }
390 |
391 | }
392 | }
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/camera/AutoFocusCallback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.camera;
15 |
16 | import android.hardware.Camera;
17 | import android.os.Handler;
18 | import android.os.Message;
19 | import android.util.Log;
20 |
21 | final class AutoFocusCallback implements Camera.AutoFocusCallback {
22 | private static final String TAG = AutoFocusCallback.class.getName();
23 | private static final long AUTO_FOCUS_INTERVAL_MS = 1500L;
24 |
25 | private Handler mAutoFocusHandler;
26 | private int mAutoFocusMessage;
27 |
28 | void setHandler(Handler autoFocusHandler, int autoFocusMessage) {
29 | this.mAutoFocusHandler = autoFocusHandler;
30 | this.mAutoFocusMessage = autoFocusMessage;
31 | }
32 |
33 | public void onAutoFocus(boolean success, Camera camera) {
34 | if (mAutoFocusHandler != null) {
35 | Message message = mAutoFocusHandler.obtainMessage(mAutoFocusMessage, success);
36 | mAutoFocusHandler.sendMessageDelayed(message, AUTO_FOCUS_INTERVAL_MS);
37 | mAutoFocusHandler = null;
38 | } else {
39 | Log.v(TAG, "Got auto-focus callback, but no handler for it");
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/camera/CameraConfigurationManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.camera;
15 |
16 | import android.content.Context;
17 | import android.graphics.Point;
18 | import android.hardware.Camera;
19 | import android.util.Log;
20 |
21 | import java.util.Collections;
22 | import java.util.Comparator;
23 | import java.util.List;
24 | import java.util.regex.Pattern;
25 |
26 | import qrcode.utils.ScreenUtils;
27 |
28 | final class CameraConfigurationManager {
29 | private static final String TAG = CameraConfigurationManager.class.getName();
30 | private static final int TEN_DESIRED_ZOOM = 10;
31 | private static final int DESIRED_SHARPNESS = 30;
32 |
33 | private static final Pattern COMMA_PATTERN = Pattern.compile(",");
34 |
35 | private Camera.Size mCameraResolution;
36 | private Camera.Size mPictureResolution;
37 | private Context mContext;
38 |
39 | CameraConfigurationManager(Context context) {
40 | this.mContext = context;
41 | }
42 |
43 | /**
44 | * Reads, one time, values from the camera that are needed by the app.
45 | */
46 | void initFromCameraParameters(Camera camera) {
47 | Camera.Parameters parameters = camera.getParameters();
48 | mCameraResolution = findCloselySize(ScreenUtils.getScreenWidth(mContext), ScreenUtils.getScreenHeight(mContext),
49 | parameters.getSupportedPreviewSizes());
50 | Log.e(TAG, "Setting preview size: " + mCameraResolution.width + "-" + mCameraResolution.height);
51 | mPictureResolution = findCloselySize(ScreenUtils.getScreenWidth(mContext),
52 | ScreenUtils.getScreenHeight(mContext), parameters.getSupportedPictureSizes());
53 | Log.e(TAG, "Setting picture size: " + mPictureResolution.width + "-" + mPictureResolution.height);
54 | }
55 |
56 | /**
57 | * Sets the camera up to take preview images which are used for both preview and decoding. We detect the preview
58 | * format here so that buildLuminanceSource() can build an appropriate LuminanceSource subclass. In the future we
59 | * may want to force YUV420SP as it's the smallest, and the planar Y can be used for barcode scanning without a copy
60 | * in some cases.
61 | */
62 | void setDesiredCameraParameters(Camera camera) {
63 |
64 | Camera.Parameters parameters = camera.getParameters();
65 | parameters.setPreviewSize(mCameraResolution.width, mCameraResolution.height);
66 | parameters.setPictureSize(mPictureResolution.width, mPictureResolution.height);
67 | setZoom(parameters);
68 | camera.setDisplayOrientation(90);
69 | camera.setParameters(parameters);
70 | }
71 |
72 | Camera.Size getCameraResolution() {
73 | return mCameraResolution;
74 | }
75 |
76 | private static Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) {
77 |
78 | String previewSizeValueString = parameters.get("preview-size-values");
79 | // saw this on Xperia
80 | if (previewSizeValueString == null) {
81 | previewSizeValueString = parameters.get("preview-size-value");
82 | }
83 |
84 | Point cameraResolution = null;
85 |
86 | if (previewSizeValueString != null) {
87 | Log.e(TAG, "preview-size-values parameter: " + previewSizeValueString);
88 | cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
89 | }
90 |
91 | if (cameraResolution == null) {
92 | // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
93 | cameraResolution = new Point((screenResolution.x >> 3) << 3, (screenResolution.y >> 3) << 3);
94 | }
95 |
96 | return cameraResolution;
97 | }
98 |
99 | private static Point findBestPreviewSizeValue(CharSequence previewSizeValueString, Point screenResolution) {
100 | int bestX = 0;
101 | int bestY = 0;
102 | int diff = Integer.MAX_VALUE;
103 | for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
104 |
105 | previewSize = previewSize.trim();
106 | int dimPosition = previewSize.indexOf('x');
107 | if (dimPosition < 0) {
108 | Log.e(TAG, "Bad preview-size: " + previewSize);
109 | continue;
110 | }
111 |
112 | int newX;
113 | int newY;
114 | try {
115 | newY = Integer.parseInt(previewSize.substring(0, dimPosition));
116 | newX = Integer.parseInt(previewSize.substring(dimPosition + 1));
117 | } catch (NumberFormatException nfe) {
118 | Log.e(TAG, "Bad preview-size: " + previewSize);
119 | continue;
120 | }
121 |
122 | int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
123 | if (newDiff == 0) {
124 | bestX = newX;
125 | bestY = newY;
126 | break;
127 | } else if (newDiff < diff) {
128 | bestX = newX;
129 | bestY = newY;
130 | diff = newDiff;
131 | }
132 |
133 | }
134 |
135 | if (bestX > 0 && bestY > 0) {
136 | return new Point(bestX, bestY);
137 | }
138 | return null;
139 | }
140 |
141 | private static int findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) {
142 | int tenBestValue = 0;
143 | for (String stringValue : COMMA_PATTERN.split(stringValues)) {
144 | stringValue = stringValue.trim();
145 | double value;
146 | try {
147 | value = Double.parseDouble(stringValue);
148 | } catch (NumberFormatException nfe) {
149 | return tenDesiredZoom;
150 | }
151 | int tenValue = (int) (10.0 * value);
152 | if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
153 | tenBestValue = tenValue;
154 | }
155 | }
156 | return tenBestValue;
157 | }
158 |
159 | private void setZoom(Camera.Parameters parameters) {
160 |
161 | String zoomSupportedString = parameters.get("zoom-supported");
162 | if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
163 | return;
164 | }
165 |
166 | int tenDesiredZoom = TEN_DESIRED_ZOOM;
167 |
168 | String maxZoomString = parameters.get("max-zoom");
169 | if (maxZoomString != null) {
170 | try {
171 | int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
172 | if (tenDesiredZoom > tenMaxZoom) {
173 | tenDesiredZoom = tenMaxZoom;
174 | }
175 | } catch (NumberFormatException nfe) {
176 | Log.e(TAG, "Bad max-zoom: " + maxZoomString);
177 | }
178 | }
179 |
180 | String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
181 | if (takingPictureZoomMaxString != null) {
182 | try {
183 | int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
184 | if (tenDesiredZoom > tenMaxZoom) {
185 | tenDesiredZoom = tenMaxZoom;
186 | }
187 | } catch (NumberFormatException nfe) {
188 | Log.e(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
189 | }
190 | }
191 |
192 | String motZoomValuesString = parameters.get("mot-zoom-values");
193 | if (motZoomValuesString != null) {
194 | tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
195 | }
196 |
197 | String motZoomStepString = parameters.get("mot-zoom-step");
198 | if (motZoomStepString != null) {
199 | try {
200 | double motZoomStep = Double.parseDouble(motZoomStepString.trim());
201 | int tenZoomStep = (int) (10.0 * motZoomStep);
202 | if (tenZoomStep > 1) {
203 | tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
204 | }
205 | } catch (NumberFormatException nfe) {
206 | // continue
207 | }
208 | }
209 |
210 | // Set zoom. This helps encourage the user to pull back.
211 | // Some devices like the Behold have a zoom parameter
212 | // if (maxZoomString != null || motZoomValuesString != null) {
213 | // parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
214 | // }
215 | if (parameters.isZoomSupported()) {
216 | Log.e(TAG, "max-zoom:" + parameters.getMaxZoom());
217 | parameters.setZoom(parameters.getMaxZoom() / 10);
218 | } else {
219 | Log.e(TAG, "Unsupported zoom.");
220 | }
221 |
222 | // Most devices, like the Hero, appear to expose this zoom parameter.
223 | // It takes on values like "27" which appears to mean 2.7x zoom
224 | // if (takingPictureZoomMaxString != null) {
225 | // parameters.set("taking-picture-zoom", tenDesiredZoom);
226 | // }
227 | }
228 |
229 | public static int getDesiredSharpness() {
230 | return DESIRED_SHARPNESS;
231 | }
232 |
233 | /**
234 | * 通过对比得到与宽高比最接近的尺寸(如果有相同尺寸,优先选择)
235 | *
236 | * @param surfaceWidth 需要被进行对比的原宽
237 | * @param surfaceHeight 需要被进行对比的原高
238 | * @param preSizeList 需要对比的预览尺寸列表
239 | * @return 得到与原宽高比例最接近的尺寸
240 | */
241 | protected Camera.Size findCloselySize(int surfaceWidth, int surfaceHeight, List preSizeList) {
242 |
243 | // // 当屏幕为垂直的时候需要把宽高值进行调换,保证宽大于高
244 | // int ReqTmpWidth = surfaceHeight;
245 | // int ReqTmpHeight = surfaceWidth;
246 | //
247 | // // 先查找preview中是否存在与SurfaceView相同宽高的尺寸
248 | // for (Size size : preSizeList) {
249 | // if ((size.width == ReqTmpWidth) && (size.height == ReqTmpHeight)) {
250 | // return size;
251 | // }
252 | // }
253 | //
254 | // // 得到与传入的宽高比最接近的size
255 | // float reqRatio = ((float) ReqTmpWidth) / ReqTmpHeight;
256 | // float curRatio, deltaRatio;
257 | // float deltaRatioMin = Float.MAX_VALUE;
258 | // Size retSize = null;
259 | // for (Size size : preSizeList) {
260 | // curRatio = ((float) size.width) / size.height;
261 | // deltaRatio = Math.abs(reqRatio - curRatio);
262 | // if (deltaRatio < deltaRatioMin) {
263 | // deltaRatioMin = deltaRatio;
264 | // retSize = size;
265 | // }
266 | // }
267 | Collections.sort(preSizeList, new SizeComparator(surfaceWidth, surfaceHeight));
268 | return preSizeList.get(0);
269 | }
270 |
271 | /**
272 | * 预览尺寸与给定的宽高尺寸比较器。首先比较宽高的比例,在宽高比相同的情况下,根据宽和高的最小差进行比较。
273 | */
274 | private static class SizeComparator implements Comparator {
275 |
276 | private final int width;
277 | private final int height;
278 | private final float ratio;
279 |
280 | SizeComparator(int width, int height) {
281 | if (width < height) {
282 | this.width = height;
283 | this.height = width;
284 | } else {
285 | this.width = width;
286 | this.height = height;
287 | }
288 | this.ratio = (float) this.height / this.width;
289 | }
290 |
291 | @Override
292 | public int compare(Camera.Size size1, Camera.Size size2) {
293 | int width1 = size1.width;
294 | int height1 = size1.height;
295 | int width2 = size2.width;
296 | int height2 = size2.height;
297 |
298 | float ratio1 = Math.abs((float) height1 / width1 - ratio);
299 | float ratio2 = Math.abs((float) height2 / width2 - ratio);
300 | int result = Float.compare(ratio1, ratio2);
301 | if (result != 0) {
302 | return result;
303 | } else {
304 | int minGap1 = Math.abs(width - width1) + Math.abs(height - height1);
305 | int minGap2 = Math.abs(width - width2) + Math.abs(height - height2);
306 | return minGap1 - minGap2;
307 | }
308 | }
309 | }
310 | }
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/camera/CameraManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.camera;
15 |
16 | import android.content.Context;
17 | import android.graphics.SurfaceTexture;
18 | import android.hardware.Camera;
19 | import android.os.Handler;
20 |
21 | import java.io.IOException;
22 | import java.util.List;
23 |
24 | /**
25 | * This object wraps the Camera service object and expects to be the only one talking to it. The implementation
26 | * encapsulates the steps needed to take preview-sized images, which are used for both preview and decoding.
27 | *
28 | */
29 | public final class CameraManager {
30 |
31 | private static CameraManager sCameraManager;
32 |
33 | private final CameraConfigurationManager mConfigManager;
34 | private Camera mCamera;
35 | private boolean mInitialized;
36 | private boolean mPreviewing;
37 | /**
38 | * Preview frames are delivered here, which we pass on to the registered handler. Make sure to clear the handler so
39 | * it will only receive one message.
40 | */
41 | private final PreviewCallback mPreviewCallback;
42 | /** Auto-focus callbacks arrive here, and are dispatched to the Handler which requested them. */
43 | private final AutoFocusCallback mAutoFocusCallback;
44 |
45 | /**
46 | * Initializes this static object with the Context of the calling Activity.
47 | */
48 | public static void init(Context context) {
49 | if (sCameraManager == null) {
50 | sCameraManager = new CameraManager(context);
51 | }
52 | }
53 |
54 | /**
55 | * Gets the CameraManager singleton instance.
56 | *
57 | * @return A reference to the CameraManager singleton.
58 | */
59 | public static CameraManager get() {
60 | return sCameraManager;
61 | }
62 |
63 | private CameraManager(Context context) {
64 | this.mConfigManager = new CameraConfigurationManager(context);
65 | mPreviewCallback = new PreviewCallback(mConfigManager);
66 | mAutoFocusCallback = new AutoFocusCallback();
67 | }
68 |
69 | /**
70 | * Opens the mCamera driver and initializes the hardware parameters.
71 | *
72 | * @param holder The surface object which the mCamera will draw preview frames into.
73 | * @throws IOException Indicates the mCamera driver failed to open.
74 | */
75 | public void openDriver(SurfaceTexture holder) throws IOException {
76 | if (mCamera == null) {
77 | mCamera = Camera.open();
78 | if (mCamera == null) {
79 | throw new IOException();
80 | }
81 | mCamera.setPreviewTexture(holder);
82 |
83 | if (!mInitialized) {
84 | mInitialized = true;
85 | mConfigManager.initFromCameraParameters(mCamera);
86 | }
87 | mConfigManager.setDesiredCameraParameters(mCamera);
88 | }
89 | }
90 |
91 | /**
92 | * 打开或关闭闪光灯
93 | *
94 | * @param open 控制是否打开
95 | * @return 打开或关闭失败,则返回false。
96 | */
97 | public boolean setFlashLight(boolean open) {
98 | if (mCamera == null) {
99 | return false;
100 | }
101 | Camera.Parameters parameters = mCamera.getParameters();
102 | if (parameters == null) {
103 | return false;
104 | }
105 | List flashModes = parameters.getSupportedFlashModes();
106 | // Check if camera flash exists
107 | if (null == flashModes || 0 == flashModes.size()) {
108 | // Use the screen as a flashlight (next best thing)
109 | return false;
110 | }
111 | String flashMode = parameters.getFlashMode();
112 | if (open) {
113 | if (Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
114 | return true;
115 | }
116 | // Turn on the flash
117 | if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
118 | parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
119 | mCamera.setParameters(parameters);
120 | return true;
121 | } else {
122 | return false;
123 | }
124 | } else {
125 | if (Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
126 | return true;
127 | }
128 | // Turn on the flash
129 | if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
130 | parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
131 | mCamera.setParameters(parameters);
132 | return true;
133 | } else
134 | return false;
135 | }
136 | }
137 |
138 | /**
139 | * Closes the camera driver if still in use.
140 | */
141 | public void closeDriver() {
142 | if (mCamera != null) {
143 | mCamera.release();
144 | mInitialized = false;
145 | mPreviewing = false;
146 | mCamera = null;
147 | }
148 | }
149 |
150 | /**
151 | * Asks the mCamera hardware to begin drawing preview frames to the screen.
152 | */
153 | public void startPreview() {
154 | if (mCamera != null && !mPreviewing) {
155 | mCamera.startPreview();
156 | mPreviewing = true;
157 | }
158 | }
159 |
160 | /**
161 | * Tells the mCamera to stop drawing preview frames.
162 | */
163 | public void stopPreview() {
164 | if (mCamera != null && mPreviewing) {
165 | mCamera.stopPreview();
166 | mPreviewCallback.setHandler(null, 0);
167 | mAutoFocusCallback.setHandler(null, 0);
168 | mPreviewing = false;
169 | }
170 | }
171 |
172 | /**
173 | * A single preview frame will be returned to the handler supplied. The data will arrive as byte[] in the
174 | * message.obj field, with width and height encoded as message.arg1 and message.arg2, respectively.
175 | *
176 | * @param handler The handler to send the message to.
177 | * @param message The what field of the message to be sent.
178 | */
179 | public void requestPreviewFrame(Handler handler, int message) {
180 | if (mCamera != null && mPreviewing) {
181 | mPreviewCallback.setHandler(handler, message);
182 | mCamera.setOneShotPreviewCallback(mPreviewCallback);
183 | }
184 | }
185 |
186 | /**
187 | * Asks the mCamera hardware to perform an autofocus.
188 | *
189 | * @param handler The Handler to notify when the autofocus completes.
190 | * @param message The message to deliver.
191 | */
192 | public void requestAutoFocus(Handler handler, int message) {
193 | if (mCamera != null && mPreviewing) {
194 | mAutoFocusCallback.setHandler(handler, message);
195 | // Log.d(TAG, "Requesting auto-focus callback");
196 | mCamera.autoFocus(mAutoFocusCallback);
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/camera/PreviewCallback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.camera;
15 |
16 | import android.hardware.Camera;
17 | import android.os.Handler;
18 | import android.os.Message;
19 | import android.util.Log;
20 |
21 | final class PreviewCallback implements Camera.PreviewCallback {
22 | private static final String TAG = PreviewCallback.class.getName();
23 | private final CameraConfigurationManager mConfigManager;
24 | private Handler mPreviewHandler;
25 | private int mPreviewMessage;
26 |
27 | PreviewCallback(CameraConfigurationManager configManager) {
28 | this.mConfigManager = configManager;
29 | }
30 |
31 | void setHandler(Handler previewHandler, int previewMessage) {
32 | this.mPreviewHandler = previewHandler;
33 | this.mPreviewMessage = previewMessage;
34 | }
35 |
36 | @Override
37 | public void onPreviewFrame(byte[] data, Camera camera) {
38 | Camera.Size cameraResolution = mConfigManager.getCameraResolution();
39 | if (mPreviewHandler != null) {
40 | Message message =
41 | mPreviewHandler.obtainMessage(mPreviewMessage, cameraResolution.width, cameraResolution.height, data);
42 | message.sendToTarget();
43 | mPreviewHandler = null;
44 | } else {
45 | Log.v(TAG, "no handler callback.");
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/decode/CaptureActivityHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.decode;
15 |
16 | import android.os.Handler;
17 | import android.os.Message;
18 | import android.util.Log;
19 |
20 | import com.ttv.livedemo.R;
21 | import com.google.zxing.Result;
22 |
23 | import qrcode.QrCodeActivity;
24 | import qrcode.camera.CameraManager;
25 |
26 | /**
27 | * This class handles all the messaging which comprises the state machine for capture.
28 | */
29 | public final class CaptureActivityHandler extends Handler {
30 | private static final String TAG = CaptureActivityHandler.class.getName();
31 |
32 | private final QrCodeActivity mActivity;
33 | private final DecodeThread mDecodeThread;
34 | private State mState;
35 |
36 | private enum State {
37 | PREVIEW, SUCCESS, DONE
38 | }
39 |
40 | public CaptureActivityHandler(QrCodeActivity activity) {
41 | this.mActivity = activity;
42 | mDecodeThread = new DecodeThread(activity);
43 | mDecodeThread.start();
44 | mState = State.SUCCESS;
45 | // Start ourselves capturing previews and decoding.
46 | restartPreviewAndDecode();
47 | }
48 |
49 | @Override
50 | public void handleMessage(Message message) {
51 | switch (message.what) {
52 | case R.id.auto_focus:
53 | // Log.d(TAG, "Got auto-focus message");
54 | // When one auto focus pass finishes, start another. This is the closest thing to
55 | // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do.
56 | if (mState == State.PREVIEW) {
57 | CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
58 | }
59 | break;
60 | case R.id.decode_succeeded:
61 | Log.e(TAG, "Got decode succeeded message");
62 | mState = State.SUCCESS;
63 | mActivity.handleDecode((Result) message.obj);
64 | break;
65 | case R.id.decode_failed:
66 | // We're decoding as fast as possible, so when one decode fails, start another.
67 | mState = State.PREVIEW;
68 | CameraManager.get().requestPreviewFrame(mDecodeThread.getHandler(), R.id.decode);
69 | break;
70 | }
71 | }
72 |
73 | public void quitSynchronously() {
74 | mState = State.DONE;
75 | CameraManager.get().stopPreview();
76 | Message quit = Message.obtain(mDecodeThread.getHandler(), R.id.quit);
77 | quit.sendToTarget();
78 | try {
79 | mDecodeThread.join();
80 | } catch (InterruptedException e) {
81 | // continue
82 | }
83 |
84 | // Be absolutely sure we don't send any queued up messages
85 | removeMessages(R.id.decode_succeeded);
86 | removeMessages(R.id.decode_failed);
87 | }
88 |
89 | public void restartPreviewAndDecode() {
90 | if (mState != State.PREVIEW) {
91 | CameraManager.get().startPreview();
92 | mState = State.PREVIEW;
93 | CameraManager.get().requestPreviewFrame(mDecodeThread.getHandler(), R.id.decode);
94 | CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
95 | }
96 | }
97 |
98 | }
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/decode/DecodeHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.decode;
15 |
16 | import android.os.Handler;
17 | import android.os.Looper;
18 | import android.os.Message;
19 |
20 | import com.ttv.livedemo.R;
21 | import com.google.zxing.BarcodeFormat;
22 | import com.google.zxing.BinaryBitmap;
23 | import com.google.zxing.DecodeHintType;
24 | import com.google.zxing.PlanarYUVLuminanceSource;
25 | import com.google.zxing.ReaderException;
26 | import com.google.zxing.Result;
27 | import com.google.zxing.common.HybridBinarizer;
28 | import com.google.zxing.qrcode.QRCodeReader;
29 |
30 | import java.util.Arrays;
31 | import java.util.Hashtable;
32 | import java.util.Map;
33 |
34 | import qrcode.QrCodeActivity;
35 |
36 | final class DecodeHandler extends Handler {
37 |
38 | private final QrCodeActivity mActivity;
39 | private final QRCodeReader mQrCodeReader;
40 | private final Map mHints;
41 | private byte[] mRotatedData;
42 |
43 | DecodeHandler(QrCodeActivity activity) {
44 | this.mActivity = activity;
45 | mQrCodeReader = new QRCodeReader();
46 | mHints = new Hashtable<>();
47 | mHints.put(DecodeHintType.CHARACTER_SET, "utf-8");
48 | mHints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
49 | mHints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);
50 | }
51 |
52 | @Override
53 | public void handleMessage(Message message) {
54 | switch (message.what) {
55 | case R.id.decode:
56 | decode((byte[]) message.obj, message.arg1, message.arg2);
57 | break;
58 | case R.id.quit:
59 | Looper looper = Looper.myLooper();
60 | if (null != looper) {
61 | looper.quit();
62 | }
63 | break;
64 | }
65 | }
66 |
67 | /**
68 | * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, reuse the same reader
69 | * objects from one decode to the next.
70 | *
71 | * @param data The YUV preview frame.
72 | * @param width The width of the preview frame.
73 | * @param height The height of the preview frame.
74 | */
75 | private void decode(byte[] data, int width, int height) {
76 | if (null == mRotatedData) {
77 | mRotatedData = new byte[width * height];
78 | } else {
79 | if (mRotatedData.length < width * height) {
80 | mRotatedData = new byte[width * height];
81 | }
82 | }
83 | Arrays.fill(mRotatedData, (byte) 0);
84 | for (int y = 0; y < height; y++) {
85 | for (int x = 0; x < width; x++) {
86 | if (x + y * width >= data.length) {
87 | break;
88 | }
89 | mRotatedData[x * height + height - y - 1] = data[x + y * width];
90 | }
91 | }
92 | int tmp = width; // Here we are swapping, that's the difference to #11
93 | width = height;
94 | height = tmp;
95 |
96 | Result rawResult = null;
97 | try {
98 | PlanarYUVLuminanceSource source =
99 | new PlanarYUVLuminanceSource(mRotatedData, width, height, 0, 0, width, height, false);
100 | BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
101 | rawResult = mQrCodeReader.decode(bitmap1, mHints);
102 | } catch (ReaderException e) {
103 | } finally {
104 | mQrCodeReader.reset();
105 | }
106 |
107 | if (rawResult != null) {
108 | Message message = Message.obtain(mActivity.getCaptureActivityHandler(), R.id.decode_succeeded, rawResult);
109 | message.sendToTarget();
110 | } else {
111 | Message message = Message.obtain(mActivity.getCaptureActivityHandler(), R.id.decode_failed);
112 | message.sendToTarget();
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/decode/DecodeImageCallback.java:
--------------------------------------------------------------------------------
1 | package qrcode.decode;
2 |
3 | import com.google.zxing.Result;
4 |
5 | /**
6 | * Created by xingli on 1/4/16.
7 | *
8 | * 图片解析二维码回调方法
9 | */
10 | public interface DecodeImageCallback {
11 |
12 | void decodeSucceed(Result result);
13 |
14 | void decodeFail(int type, String reason);
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/decode/DecodeImageThread.java:
--------------------------------------------------------------------------------
1 | package qrcode.decode;
2 |
3 | import android.graphics.Bitmap;
4 | import android.text.TextUtils;
5 |
6 | import com.google.zxing.Result;
7 |
8 | import qrcode.utils.QrUtils;
9 |
10 | /**
11 | * Created by xingli on 1/4/16.
12 | *
13 | * 解析图像二维码线程
14 | */
15 | public class DecodeImageThread implements Runnable {
16 | private static final int MAX_PICTURE_PIXEL = 256;
17 | private byte[] mData;
18 | private int mWidth;
19 | private int mHeight;
20 | private String mImgPath;
21 | private DecodeImageCallback mCallback;
22 |
23 | public DecodeImageThread(String imgPath, DecodeImageCallback callback) {
24 | this.mImgPath = imgPath;
25 | this.mCallback = callback;
26 | }
27 |
28 | @Override
29 | public void run() {
30 | if (null == mData) {
31 | if (!TextUtils.isEmpty(mImgPath)) {
32 | Bitmap bitmap = QrUtils.decodeSampledBitmapFromFile(mImgPath, MAX_PICTURE_PIXEL, MAX_PICTURE_PIXEL);
33 | this.mData = QrUtils.getYUV420sp(bitmap.getWidth(), bitmap.getHeight(), bitmap);
34 | this.mWidth = bitmap.getWidth();
35 | this.mHeight = bitmap.getHeight();
36 | }
37 | }
38 |
39 | if (mData == null || mData.length == 0 || mWidth == 0 || mHeight == 0) {
40 | if (null != mCallback) {
41 | mCallback.decodeFail(0, "No image data");
42 | }
43 | return;
44 | }
45 |
46 | final Result result = QrUtils.decodeImage(mData, mWidth, mHeight);
47 |
48 | if (null != mCallback) {
49 | if (null != result) {
50 | mCallback.decodeSucceed(result);
51 | } else {
52 | mCallback.decodeFail(0, "Decode image failed.");
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/decode/DecodeManager.java:
--------------------------------------------------------------------------------
1 | package qrcode.decode;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 |
8 | import com.ttv.livedemo.R;
9 |
10 |
11 | /**
12 | * Created by xingli on 1/8/16.
13 | *
14 | * 二维码解析管理。
15 | */
16 | public class DecodeManager {
17 |
18 | public void showPermissionDeniedDialog(Context context) {
19 | // 权限在安装时被关闭了,如小米手机
20 | new AlertDialog.Builder(context).setTitle(R.string.qr_code_notification)
21 | .setMessage(R.string.qr_code_camera_not_open)
22 | .setPositiveButton(R.string.qr_code_positive_button_know, new DialogInterface.OnClickListener() {
23 | @Override
24 | public void onClick(DialogInterface dialog, int which) {
25 | dialog.dismiss();
26 | }
27 | }).show();
28 | }
29 |
30 | public void showResultDialog(Activity activity, String resultString, DialogInterface.OnClickListener listener) {
31 | new AlertDialog.Builder(activity).setTitle(R.string.qr_code_notification).setMessage(resultString)
32 | .setPositiveButton(R.string.qr_code_positive_button_confirm, listener).show();
33 | }
34 |
35 | public void showCouldNotReadQrCodeFromScanner(Context context, final OnRefreshCameraListener listener) {
36 | new AlertDialog.Builder(context).setTitle(R.string.qr_code_notification)
37 | .setMessage(R.string.qr_code_could_not_read_qr_code_from_scanner)
38 | .setPositiveButton(R.string.qc_code_close, new DialogInterface.OnClickListener() {
39 | @Override
40 | public void onClick(DialogInterface dialog, int which) {
41 | dialog.dismiss();
42 | if (listener != null) {
43 | listener.refresh();
44 | }
45 | }
46 | }).show();
47 | }
48 |
49 | public void showCouldNotReadQrCodeFromPicture(Context context) {
50 | new AlertDialog.Builder(context).setTitle(R.string.qr_code_notification)
51 | .setMessage(R.string.qr_code_could_not_read_qr_code_from_picture)
52 | .setPositiveButton(R.string.qc_code_close, new DialogInterface.OnClickListener() {
53 | @Override
54 | public void onClick(DialogInterface dialog, int which) {
55 | dialog.dismiss();
56 | }
57 | }).show();
58 | }
59 |
60 | public interface OnRefreshCameraListener {
61 | void refresh();
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/decode/DecodeThread.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.decode;
15 |
16 | import android.os.Handler;
17 | import android.os.Looper;
18 |
19 | import java.util.concurrent.CountDownLatch;
20 |
21 | import qrcode.QrCodeActivity;
22 |
23 |
24 | /**
25 | * This thread does all the heavy lifting of decoding the images.
26 | */
27 | final class DecodeThread extends Thread {
28 |
29 | private final QrCodeActivity mActivity;
30 | private Handler mHandler;
31 | private final CountDownLatch mHandlerInitLatch;
32 |
33 | DecodeThread(QrCodeActivity activity) {
34 | this.mActivity = activity;
35 | mHandlerInitLatch = new CountDownLatch(1);
36 | }
37 |
38 | Handler getHandler() {
39 | try {
40 | mHandlerInitLatch.await();
41 | } catch (InterruptedException ie) {
42 | // continue?
43 | }
44 | return mHandler;
45 | }
46 |
47 | @Override
48 | public void run() {
49 | Looper.prepare();
50 | mHandler = new DecodeHandler(mActivity);
51 | mHandlerInitLatch.countDown();
52 | Looper.loop();
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/decode/FinishListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.decode;
15 |
16 | import android.app.Activity;
17 | import android.content.DialogInterface;
18 |
19 | /**
20 | * Simple listener used to exit the app in a few cases.
21 | *
22 | */
23 | public final class FinishListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
24 | Runnable {
25 |
26 | private final Activity mActivityToFinish;
27 |
28 | public FinishListener(Activity activityToFinish) {
29 | this.mActivityToFinish = activityToFinish;
30 | }
31 |
32 | public void onCancel(DialogInterface dialogInterface) {
33 | run();
34 | }
35 |
36 | public void onClick(DialogInterface dialogInterface, int i) {
37 | run();
38 | }
39 |
40 | public void run() {
41 | mActivityToFinish.finish();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/decode/InactivityTimer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.decode;
15 |
16 | import android.app.Activity;
17 |
18 | import java.util.concurrent.Executors;
19 | import java.util.concurrent.ScheduledExecutorService;
20 | import java.util.concurrent.ScheduledFuture;
21 | import java.util.concurrent.ThreadFactory;
22 | import java.util.concurrent.TimeUnit;
23 | import androidx.annotation.NonNull;
24 | /**
25 | * Finishes an activity after a period of inactivity.
26 | */
27 | public final class InactivityTimer {
28 |
29 | private static final int INACTIVITY_DELAY_SECONDS = 5 * 60;
30 |
31 | private final ScheduledExecutorService inactivityTimer = Executors
32 | .newSingleThreadScheduledExecutor(new DaemonThreadFactory());
33 | private final Activity activity;
34 | private ScheduledFuture> inactivityFuture = null;
35 |
36 | public InactivityTimer(Activity activity) {
37 | this.activity = activity;
38 | onActivity();
39 | }
40 |
41 | public void onActivity() {
42 | cancel();
43 | inactivityFuture =
44 | inactivityTimer.schedule(new FinishListener(activity), INACTIVITY_DELAY_SECONDS, TimeUnit.SECONDS);
45 | }
46 |
47 | private void cancel() {
48 | if (inactivityFuture != null) {
49 | inactivityFuture.cancel(true);
50 | inactivityFuture = null;
51 | }
52 | }
53 |
54 | public void shutdown() {
55 | cancel();
56 | inactivityTimer.shutdown();
57 | }
58 |
59 | private static final class DaemonThreadFactory implements ThreadFactory {
60 | public Thread newThread(@NonNull Runnable runnable) {
61 | Thread thread = new Thread(runnable);
62 | thread.setDaemon(true);
63 | return thread;
64 | }
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/utils/QrUtils.java:
--------------------------------------------------------------------------------
1 | package qrcode.utils;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 |
6 | import com.google.zxing.BarcodeFormat;
7 | import com.google.zxing.BinaryBitmap;
8 | import com.google.zxing.DecodeHintType;
9 | import com.google.zxing.PlanarYUVLuminanceSource;
10 | import com.google.zxing.ReaderException;
11 | import com.google.zxing.Result;
12 | import com.google.zxing.common.GlobalHistogramBinarizer;
13 | import com.google.zxing.qrcode.QRCodeReader;
14 |
15 | import java.util.Arrays;
16 | import java.util.Hashtable;
17 |
18 | /**
19 | * Created by xingli on 12/25/15.
20 | *
21 | * 二维码相关功能类
22 | */
23 | public class QrUtils {
24 | private static byte[] yuvs;
25 |
26 | /**
27 | * YUV420sp
28 | *
29 | * @param inputWidth
30 | * @param inputHeight
31 | * @param scaled
32 | * @return
33 | */
34 | public static byte[] getYUV420sp(int inputWidth, int inputHeight, Bitmap scaled) {
35 | int[] argb = new int[inputWidth * inputHeight];
36 |
37 | scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
38 |
39 | /**
40 | * 需要转换成偶数的像素点,否则编码YUV420的时候有可能导致分配的空间大小不够而溢出。
41 | */
42 | int requiredWidth = inputWidth % 2 == 0 ? inputWidth : inputWidth + 1;
43 | int requiredHeight = inputHeight % 2 == 0 ? inputHeight : inputHeight + 1;
44 |
45 | int byteLength = requiredWidth * requiredHeight * 3 / 2;
46 | if (yuvs == null || yuvs.length < byteLength) {
47 | yuvs = new byte[byteLength];
48 | } else {
49 | Arrays.fill(yuvs, (byte) 0);
50 | }
51 |
52 | encodeYUV420SP(yuvs, argb, inputWidth, inputHeight);
53 |
54 | scaled.recycle();
55 |
56 | return yuvs;
57 | }
58 |
59 | /**
60 | * RGB转YUV420sp
61 | *
62 | * @param yuv420sp inputWidth * inputHeight * 3 / 2
63 | * @param argb inputWidth * inputHeight
64 | * @param width
65 | * @param height
66 | */
67 | private static void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
68 | // 帧图片的像素大小
69 | final int frameSize = width * height;
70 | // ---YUV数据---
71 | int Y, U, V;
72 | // Y的index从0开始
73 | int yIndex = 0;
74 | // UV的index从frameSize开始
75 | int uvIndex = frameSize;
76 |
77 | // ---颜色数据---
78 | // int a, R, G, B;
79 | int R, G, B;
80 | //
81 | int argbIndex = 0;
82 | //
83 |
84 | // ---循环所有像素点,RGB转YUV---
85 | for (int j = 0; j < height; j++) {
86 | for (int i = 0; i < width; i++) {
87 |
88 | // a is not used obviously
89 | // a = (argb[argbIndex] & 0xff000000) >> 24;
90 | R = (argb[argbIndex] & 0xff0000) >> 16;
91 | G = (argb[argbIndex] & 0xff00) >> 8;
92 | B = (argb[argbIndex] & 0xff);
93 | //
94 | argbIndex++;
95 |
96 | // well known RGB to YUV algorithm
97 | Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
98 | U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
99 | V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
100 |
101 | //
102 | Y = Math.max(0, Math.min(Y, 255));
103 | U = Math.max(0, Math.min(U, 255));
104 | V = Math.max(0, Math.min(V, 255));
105 |
106 | // NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
107 | // meaning for every 4 Y pixels there are 1 V and 1 U. Note the sampling is every other
108 | // pixel AND every other scanline.
109 | // ---Y---
110 | yuv420sp[yIndex++] = (byte) Y;
111 | // ---UV---
112 | if ((j % 2 == 0) && (i % 2 == 0)) {
113 | //
114 | yuv420sp[uvIndex++] = (byte) V;
115 | //
116 | yuv420sp[uvIndex++] = (byte) U;
117 | }
118 | }
119 | }
120 | }
121 |
122 | public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
123 | // Raw height and width of image
124 | final int height = options.outHeight;
125 | final int width = options.outWidth;
126 | int inSampleSize = 1;
127 |
128 | if (height > reqHeight || width > reqWidth) {
129 |
130 | final int halfHeight = height / 2;
131 | final int halfWidth = width / 2;
132 |
133 | // Calculate the largest inSampleSize value that is a power of 2 and keeps both
134 | // height and width larger than the requested height and width.
135 | while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
136 | inSampleSize *= 2;
137 | }
138 | }
139 |
140 | return inSampleSize;
141 | }
142 |
143 | public static Bitmap decodeSampledBitmapFromFile(String imgPath, int reqWidth, int reqHeight) {
144 |
145 | // First decode with inJustDecodeBounds=true to check dimensions
146 | final BitmapFactory.Options options = new BitmapFactory.Options();
147 | options.inJustDecodeBounds = true;
148 | BitmapFactory.decodeFile(imgPath, options);
149 |
150 | // Calculate inSampleSize
151 | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
152 |
153 | // Decode bitmap with inSampleSize set
154 | options.inJustDecodeBounds = false;
155 | return BitmapFactory.decodeFile(imgPath, options);
156 | }
157 |
158 |
159 | /**
160 | * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, reuse the same reader
161 | * objects from one decode to the next.
162 | */
163 | public static Result decodeImage(byte[] data, int width, int height) {
164 | // 处理
165 | Result result = null;
166 | try {
167 | Hashtable hints = new Hashtable();
168 | hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
169 | hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
170 | hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);
171 | PlanarYUVLuminanceSource source =
172 | new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false);
173 | /**
174 | * HybridBinarizer算法使用了更高级的算法,但使用GlobalHistogramBinarizer识别效率确实比HybridBinarizer要高一些。
175 | *
176 | * GlobalHistogram算法:(http://kuangjianwei.blog.163.com/blog/static/190088953201361015055110/)
177 | *
178 | * 二值化的关键就是定义出黑白的界限,我们的图像已经转化为了灰度图像,每个点都是由一个灰度值来表示,就需要定义出一个灰度值,大于这个值就为白(0),低于这个值就为黑(1)。
179 | * 在GlobalHistogramBinarizer中,是从图像中均匀取5行(覆盖整个图像高度),每行取中间五分之四作为样本;以灰度值为X轴,每个灰度值的像素个数为Y轴建立一个直方图,
180 | * 从直方图中取点数最多的一个灰度值,然后再去给其他的灰度值进行分数计算,按照点数乘以与最多点数灰度值的距离的平方来进行打分,选分数最高的一个灰度值。接下来在这两个灰度值中间选取一个区分界限,
181 | * 取的原则是尽量靠近中间并且要点数越少越好。界限有了以后就容易了,与整幅图像的每个点进行比较,如果灰度值比界限小的就是黑,在新的矩阵中将该点置1,其余的就是白,为0。
182 | */
183 | BinaryBitmap bitmap1 = new BinaryBitmap(new GlobalHistogramBinarizer(source));
184 | // BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
185 | QRCodeReader reader2 = new QRCodeReader();
186 | result = reader2.decode(bitmap1, hints);
187 | } catch (ReaderException e) {
188 | }
189 | return result;
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/utils/ScreenUtils.java:
--------------------------------------------------------------------------------
1 | package qrcode.utils;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 |
6 | /**
7 | * ScreenUtils
8 | *
9 | * Convert between dp and sp
10 | *
11 | *
12 | * @author Trinea 2014-2-14
13 | */
14 | public class ScreenUtils {
15 |
16 | private ScreenUtils() {
17 | throw new AssertionError();
18 | }
19 |
20 | /**
21 | * 获取屏幕宽度
22 | *
23 | * @return
24 | */
25 | public static int getScreenWidth(Context context) {
26 |
27 | DisplayMetrics dm = context.getResources().getDisplayMetrics();
28 | return dm.widthPixels;
29 | }
30 |
31 | /**
32 | * 获取屏幕高度
33 | *
34 | * @return
35 | */
36 | public static int getScreenHeight(Context context) {
37 | DisplayMetrics dm = context.getResources().getDisplayMetrics();
38 | int screenHeight = dm.heightPixels;
39 | return screenHeight;
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/qrcode/view/QrCodeFinderView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008 ZXing authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 | * the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 | * specific language governing permissions and limitations under the License.
12 | */
13 |
14 | package qrcode.view;
15 |
16 | import android.content.Context;
17 | import android.content.res.Resources;
18 | import android.graphics.Canvas;
19 | import android.graphics.Paint;
20 | import android.graphics.Rect;
21 | import android.util.AttributeSet;
22 | import android.view.LayoutInflater;
23 | import android.widget.FrameLayout;
24 | import android.widget.RelativeLayout;
25 |
26 |
27 | import com.ttv.livedemo.R;
28 |
29 | import qrcode.utils.ScreenUtils;
30 |
31 | /**
32 | * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial transparency outside
33 | * it, as well as the laser scanner animation and result points.
34 | */
35 | public final class QrCodeFinderView extends RelativeLayout {
36 |
37 | private static final int[] SCANNER_ALPHA = { 0, 64, 128, 192, 255, 192, 128, 64 };
38 | private static final long ANIMATION_DELAY = 100L;
39 | private static final int OPAQUE = 0xFF;
40 |
41 | private Context mContext;
42 | private Paint mPaint;
43 | private int mScannerAlpha;
44 | private int mMaskColor;
45 | private int mFrameColor;
46 | private int mLaserColor;
47 | private int mTextColor;
48 | private Rect mFrameRect;
49 | private int mFocusThick;
50 | private int mAngleThick;
51 | private int mAngleLength;
52 |
53 | public QrCodeFinderView(Context context) {
54 | this(context, null);
55 | }
56 |
57 | public QrCodeFinderView(Context context, AttributeSet attrs) {
58 | this(context, attrs, 0);
59 | }
60 |
61 | public QrCodeFinderView(Context context, AttributeSet attrs, int defStyleAttr) {
62 | super(context, attrs, defStyleAttr);
63 | mContext = context;
64 | mPaint = new Paint();
65 |
66 | Resources resources = getResources();
67 | mMaskColor = resources.getColor(R.color.qr_code_finder_mask);
68 | mFrameColor = resources.getColor(R.color.qr_code_finder_frame);
69 | mLaserColor = resources.getColor(R.color.qr_code_finder_laser);
70 | mTextColor = resources.getColor(R.color.qr_code_white);
71 |
72 | mFocusThick = 1;
73 | mAngleThick = 8;
74 | mAngleLength = 40;
75 | mScannerAlpha = 0;
76 | init(context);
77 | }
78 |
79 | private void init(Context context) {
80 | if (isInEditMode()) {
81 | return;
82 | }
83 | // 需要调用下面的方法才会执行onDraw方法
84 | setWillNotDraw(false);
85 | LayoutInflater inflater = LayoutInflater.from(context);
86 | RelativeLayout relativeLayout = (RelativeLayout) inflater.inflate(R.layout.layout_qr_code_scanner, this);
87 | FrameLayout frameLayout = (FrameLayout) relativeLayout.findViewById(R.id.qr_code_fl_scanner);
88 | mFrameRect = new Rect();
89 | LayoutParams layoutParams = (LayoutParams) frameLayout.getLayoutParams();
90 | mFrameRect.left = (ScreenUtils.getScreenWidth(context) - layoutParams.width) / 2;
91 | mFrameRect.top = layoutParams.topMargin;
92 | mFrameRect.right = mFrameRect.left + layoutParams.width;
93 | mFrameRect.bottom = mFrameRect.top + layoutParams.height;
94 | }
95 |
96 | @Override
97 | public void onDraw(Canvas canvas) {
98 | if (isInEditMode()) {
99 | return;
100 | }
101 | Rect frame = mFrameRect;
102 | if (frame == null) {
103 | return;
104 | }
105 | int width = canvas.getWidth();
106 | int height = canvas.getHeight();
107 |
108 | // 绘制焦点框外边的暗色背景
109 | mPaint.setColor(mMaskColor);
110 | canvas.drawRect(0, 0, width, frame.top, mPaint);
111 | canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, mPaint);
112 | canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, mPaint);
113 | canvas.drawRect(0, frame.bottom + 1, width, height, mPaint);
114 |
115 | drawFocusRect(canvas, frame);
116 | drawAngle(canvas, frame);
117 | drawText(canvas, frame);
118 | drawLaser(canvas, frame);
119 |
120 | // Request another update at the animation interval, but only repaint the laser line,
121 | // not the entire viewfinder mask.
122 | postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
123 | }
124 |
125 | /**
126 | * 画聚焦框,白色的
127 | *
128 | * @param canvas
129 | * @param rect
130 | */
131 | private void drawFocusRect(Canvas canvas, Rect rect) {
132 | // 绘制焦点框(黑色)
133 | mPaint.setColor(mFrameColor);
134 | // 上
135 | canvas.drawRect(rect.left + mAngleLength, rect.top, rect.right - mAngleLength, rect.top + mFocusThick, mPaint);
136 | // 左
137 | canvas.drawRect(rect.left, rect.top + mAngleLength, rect.left + mFocusThick, rect.bottom - mAngleLength,
138 | mPaint);
139 | // 右
140 | canvas.drawRect(rect.right - mFocusThick, rect.top + mAngleLength, rect.right, rect.bottom - mAngleLength,
141 | mPaint);
142 | // 下
143 | canvas.drawRect(rect.left + mAngleLength, rect.bottom - mFocusThick, rect.right - mAngleLength, rect.bottom,
144 | mPaint);
145 | }
146 |
147 | /**
148 | * 画粉色的四个角
149 | *
150 | * @param canvas
151 | * @param rect
152 | */
153 | private void drawAngle(Canvas canvas, Rect rect) {
154 | mPaint.setColor(mLaserColor);
155 | mPaint.setAlpha(OPAQUE);
156 | mPaint.setStyle(Paint.Style.FILL);
157 | mPaint.setStrokeWidth(mAngleThick);
158 | int left = rect.left;
159 | int top = rect.top;
160 | int right = rect.right;
161 | int bottom = rect.bottom;
162 | // 左上角
163 | canvas.drawRect(left, top, left + mAngleLength, top + mAngleThick, mPaint);
164 | canvas.drawRect(left, top, left + mAngleThick, top + mAngleLength, mPaint);
165 | // 右上角
166 | canvas.drawRect(right - mAngleLength, top, right, top + mAngleThick, mPaint);
167 | canvas.drawRect(right - mAngleThick, top, right, top + mAngleLength, mPaint);
168 | // 左下角
169 | canvas.drawRect(left, bottom - mAngleLength, left + mAngleThick, bottom, mPaint);
170 | canvas.drawRect(left, bottom - mAngleThick, left + mAngleLength, bottom, mPaint);
171 | // 右下角
172 | canvas.drawRect(right - mAngleLength, bottom - mAngleThick, right, bottom, mPaint);
173 | canvas.drawRect(right - mAngleThick, bottom - mAngleLength, right, bottom, mPaint);
174 | }
175 |
176 | private void drawText(Canvas canvas, Rect rect) {
177 | int margin = 40;
178 | mPaint.setColor(mTextColor);
179 | mPaint.setTextSize(getResources().getDimension(R.dimen.text_size_13sp));
180 | String text = getResources().getString(R.string.qr_code_auto_scan_notification);
181 | Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
182 | float fontTotalHeight = fontMetrics.bottom - fontMetrics.top;
183 | float offY = fontTotalHeight / 2 - fontMetrics.bottom;
184 | float newY = rect.bottom + margin + offY;
185 | float left = (ScreenUtils.getScreenWidth(mContext) - mPaint.getTextSize() * text.length()) / 2;
186 | canvas.drawText(text, left, newY, mPaint);
187 | }
188 |
189 | private void drawLaser(Canvas canvas, Rect rect) {
190 | // 绘制焦点框内固定的一条扫描线(红色)
191 | mPaint.setColor(mLaserColor);
192 | mPaint.setAlpha(SCANNER_ALPHA[mScannerAlpha]);
193 | mScannerAlpha = (mScannerAlpha + 1) % SCANNER_ALPHA.length;
194 | int middle = rect.height() / 2 + rect.top;
195 | canvas.drawRect(rect.left + 2, middle - 1, rect.right - 1, middle + 2, mPaint);
196 |
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fade_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/drawable/back.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_credit_card_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_camera.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/drawable/ic_done.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_gallery.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_history.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_id_front.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/drawable/ic_id_front.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_outline_contact_mail_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_outline_credit_card_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_outline_photo_camera_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_refresh.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_start.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_stop.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/surlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/drawable/surlogo.png
--------------------------------------------------------------------------------
/app/src/main/res/font/poppins_medium.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_activation.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
26 |
27 |
30 |
31 |
40 |
41 |
50 |
51 |
52 |
57 |
58 |
67 |
68 |
77 |
78 |
79 |
84 |
85 |
90 |
91 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
47 |
48 |
49 |
62 |
63 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_qr_code.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
19 |
20 |
21 |
22 |
29 |
30 |
36 |
37 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_qr_code_scanner.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaceOnLive/Face-Liveness-Detection-SDK-Android/3c771cdd8bff26afb4f2019bfcdf873db43fe91b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #d26159
5 | #d26159
6 | #d26159
7 | #d26159
8 | #FF000000
9 | #FFFFFFFF
10 |
11 | #FFFFFFFF
12 | #66000000
13 | #80000000
14 | #66FFFFFF
15 | #FFFFFFFF
16 | #ef8394
17 |
18 | #40000000
19 | #A0000000
20 | #00FFFFFF
21 | @color/white
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 13dp
4 | 54dp
5 | 16dp
6 | 16dp
7 |
8 | 1dp
9 | 12dp
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/font_certs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @array/com_google_android_gms_fonts_certs_dev
5 | - @array/com_google_android_gms_fonts_certs_prod
6 |
7 |
8 | -
9 | MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
10 |
11 |
12 |
13 | -
14 | MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/preloaded_fonts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @font/poppins_medium
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | TTVLiveDemo
3 |
4 | Take Selfie
5 | Scan ID Card Front
6 | Scan ID Card Back
7 |
8 | Liveness Detection
9 |
10 | Scan QrCode
11 | Close
12 | Select Picture
13 | Open Flash Light
14 | Close Flash Light
15 |
16 | Notification
17 | Camera not open!
18 | Ok
19 | Confirm
20 | Could not read qrcode from picture
21 | Could not read qrcode from scanner
22 | Camera not found
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/livedemo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ttv.livedemo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:4.2.1"
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | mavenCentral()
19 | jcenter() // Warning: this repository is going to shut down soon
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = "TTVLiveDemo"
2 | include ':app'
3 |
--------------------------------------------------------------------------------