/* * Copyright 2016 Hippo Seven * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hippo.ehviewer.ui.scene; import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.ui.MainActivity; import com.hippo.hardware.ShakeDetector; import com.hippo.widget.lockpattern.LockPatternUtils; import com.hippo.widget.lockpattern.LockPatternView; import com.hippo.yorozuya.ObjectUtils; import com.hippo.yorozuya.ViewUtils; import junit.framework.Assert; import java.util.List; public class SecurityScene extends SolidScene implements LockPatternView.OnPatternListener, ShakeDetector.OnShakeListener { private static final int MAX_RETRY_TIMES = 5; private static final long ERROR_TIMEOUT_MILLIS = 1200; private static final long SUCCESS_DELAY_MILLIS = 100; private static final String KEY_RETRY_TIMES = "retry_times"; @Nullable private LockPatternView mPatternView; private ImageView mFingerprintIcon; private SensorManager mSensorManager; private Sensor mAccelerometer; private ShakeDetector mShakeDetector; private FingerprintManager mFingerprintManager; private CancellationSignal mFingerprintCancellationSignal; private int mRetryTimes; @Override public boolean needShowLeftDrawer() { return false; } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Context context = getContext2(); Assert.assertNotNull(context); mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); if (null != mSensorManager) { mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (null != mAccelerometer) { mShakeDetector = new ShakeDetector(); mShakeDetector.setOnShakeListener(this); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { mFingerprintManager = context.getSystemService(FingerprintManager.class); } if (null == savedInstanceState) { mRetryTimes = MAX_RETRY_TIMES; } else { mRetryTimes = savedInstanceState.getInt(KEY_RETRY_TIMES); } } @Override public void onDestroy() { super.onDestroy(); mSensorManager = null; mAccelerometer = null; mShakeDetector = null; } @Override public void onResume() { super.onResume(); if (null != mShakeDetector) { mSensorManager.registerListener(mShakeDetector, mAccelerometer, SensorManager.SENSOR_DELAY_UI); } if (isFingerprintAuthAvailable()) { mFingerprintCancellationSignal = new CancellationSignal(); // The line below prevents the false positive inspection from Android Studio // noinspection ResourceType mFingerprintManager.authenticate(null, mFingerprintCancellationSignal, 0, new FingerprintManager.AuthenticationCallback() { @Override public void onAuthenticationError(int errMsgId, CharSequence errString) { fingerprintError(true); } @Override public void onAuthenticationFailed() { fingerprintError(false); } @Override public void onAuthenticationSucceeded( FingerprintManager.AuthenticationResult result) { mFingerprintIcon.setImageResource(R.drawable.ic_fingerprint_success); mFingerprintIcon.postDelayed(new Runnable() { @Override public void run() { startSceneForCheckStep(CHECK_STEP_SECURITY, getArguments()); finish(); } }, SUCCESS_DELAY_MILLIS); } }, null); } } @Override public void onPause() { super.onPause(); if (null != mShakeDetector) { mSensorManager.unregisterListener(mShakeDetector); } if (isFingerprintAuthAvailable() && mFingerprintCancellationSignal != null) { mFingerprintCancellationSignal.cancel(); mFingerprintCancellationSignal = null; } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(KEY_RETRY_TIMES, mRetryTimes); } @Nullable @Override public View onCreateView2(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_security, container, false); mPatternView = (LockPatternView) ViewUtils.$$(view, R.id.pattern_view); mPatternView.setOnPatternListener(this); mFingerprintIcon = (ImageView) ViewUtils.$$(view, R.id.fingerprint_icon); if (Settings.getEnableFingerprint() && isFingerprintAuthAvailable()) { mFingerprintIcon.setVisibility(View.VISIBLE); mFingerprintIcon.setImageResource(R.drawable.ic_fp_40px); } return view; } @Override public void onDestroyView() { super.onDestroyView(); mPatternView = null; } @Override public void onPatternStart() {} @Override public void onPatternCleared() {} @Override public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {} @Override public void onPatternDetected(List<LockPatternView.Cell> pattern) { MainActivity activity = getActivity2(); if (null == activity || null == mPatternView) { return; } String enteredPatter = LockPatternUtils.patternToString(pattern); String targetPatter = Settings.getSecurity(); if (ObjectUtils.equal(enteredPatter, targetPatter)) { startSceneForCheckStep(CHECK_STEP_SECURITY, getArguments()); finish(); } else { mPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); mRetryTimes--; if (mRetryTimes <= 0) { finish(); } } } @Override public void onShake(int count) { if (count == 10) { MainActivity activity = getActivity2(); if (null == activity) { return; } Settings.putSecurity(""); startSceneForCheckStep(CHECK_STEP_SECURITY, getArguments()); finish(); } } private boolean isFingerprintAuthAvailable() { // The line below prevents the false positive inspection from Android Studio // noinspection ResourceType return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.getEnableFingerprint() && mFingerprintManager != null && mFingerprintManager.isHardwareDetected() && mFingerprintManager.hasEnrolledFingerprints(); } private Runnable mResetFingerprintRunnable = new Runnable() { @Override public void run() { if (mFingerprintIcon != null) mFingerprintIcon.setImageResource(R.drawable.ic_fp_40px); } }; private void fingerprintError(boolean unrecoverable) { // Do not decrease mRetryTimes here since Android system will handle it :) mFingerprintIcon.setImageResource(R.drawable.ic_fingerprint_error); mFingerprintIcon.removeCallbacks(mResetFingerprintRunnable); if (unrecoverable) { mFingerprintIcon.postDelayed(new Runnable() { @Override public void run() { mFingerprintIcon.setVisibility(View.INVISIBLE); } }, ERROR_TIMEOUT_MILLIS); } else { mFingerprintIcon.postDelayed(mResetFingerprintRunnable, ERROR_TIMEOUT_MILLIS); } } }