Add fingerprint to lock UI

Migrate login UI to conductor
Fix batch add controller not saving EditText content onResume
Prevent double-locking of lock UI
Remove back button from lock UI
Fix login preference not updating
This commit is contained in:
NerdNumber9
2017-08-24 17:11:43 -04:00
parent 32d02f9329
commit b20b3d6f5c
21 changed files with 438 additions and 109 deletions

View File

@@ -0,0 +1,149 @@
package exh.ui.lock
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.support.v7.preference.SwitchPreferenceCompat
import android.support.v7.widget.LinearLayoutCompat
import android.util.AttributeSet
import android.view.Gravity
import android.view.ViewGroup
import android.widget.TextView
import com.afollestad.materialdialogs.MaterialDialog
import com.github.ajalt.reprint.core.AuthenticationResult
import com.github.ajalt.reprint.core.Reprint
import com.github.ajalt.reprint.rxjava.RxReprint
import com.mattprecious.swirl.SwirlView
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.setting.onChange
import exh.util.dpToPx
import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.injectLazy
class FingerLockPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
SwitchPreferenceCompat(context, attrs) {
val prefs: PreferencesHelper by injectLazy()
val fingerprintSupported
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& Reprint.isHardwarePresent()
&& Reprint.hasFingerprintRegistered()
val useFingerprint
get() = fingerprintSupported
&& prefs.lockUseFingerprint().getOrDefault()
@SuppressLint("NewApi")
override fun onAttached() {
super.onAttached()
if(fingerprintSupported) {
updateSummary()
onChange {
if(it as Boolean)
tryChange()
else
prefs.lockUseFingerprint().set(false)
!it
}
} else {
title = "Fingerprint unsupported"
shouldDisableView = true
summary = if(!Reprint.hasFingerprintRegistered())
"No fingerprints enrolled!"
else
"Fingerprint unlock is unsupported on this device!"
onChange { false }
}
}
private fun updateSummary() {
isChecked = useFingerprint
title = if(isChecked)
"Fingerprint enabled"
else
"Fingerprint disabled"
}
@TargetApi(Build.VERSION_CODES.M)
fun tryChange() {
val statusTextView = TextView(context).apply {
text = "Please touch the fingerprint sensor"
val size = ViewGroup.LayoutParams.WRAP_CONTENT
layoutParams = (layoutParams ?: ViewGroup.LayoutParams(
size, size
)).apply {
width = size
height = size
setPadding(0, 0, dpToPx(context, 8), 0)
}
}
val iconView = SwirlView(context).apply {
val size = dpToPx(context, 30)
layoutParams = (layoutParams ?: ViewGroup.LayoutParams(
size, size
)).apply {
width = size
height = size
}
setState(SwirlView.State.OFF, false)
}
val linearLayout = LinearLayoutCompat(context).apply {
orientation = LinearLayoutCompat.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
val size = LinearLayoutCompat.LayoutParams.WRAP_CONTENT
layoutParams = (layoutParams ?: LinearLayoutCompat.LayoutParams(
size, size
)).apply {
width = size
height = size
val pSize = dpToPx(context, 24)
setPadding(pSize, 0, pSize, 0)
}
addView(statusTextView)
addView(iconView)
}
val dialog = MaterialDialog.Builder(context)
.title("Fingerprint verification")
.customView(linearLayout, false)
.negativeText("Cancel")
.autoDismiss(true)
.cancelable(true)
.canceledOnTouchOutside(true)
.show()
iconView.setState(SwirlView.State.ON)
val subscription = RxReprint.authenticate()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { result ->
when (result.status) {
AuthenticationResult.Status.SUCCESS -> {
iconView.setState(SwirlView.State.ON)
prefs.lockUseFingerprint().set(true)
dialog.dismiss()
updateSummary()
}
AuthenticationResult.Status.NONFATAL_FAILURE -> {
iconView.setState(SwirlView.State.ERROR)
statusTextView.text = result.errorMessage
}
AuthenticationResult.Status.FATAL_FAILURE, null -> {
MaterialDialog.Builder(context)
.title("Fingerprint verification failed!")
.content(result.errorMessage)
.positiveText("Ok")
.autoDismiss(true)
.cancelable(true)
.canceledOnTouchOutside(false)
.show()
dialog.dismiss()
}
}
}
dialog.setOnDismissListener {
subscription.unsubscribe()
}
}
}

View File

@@ -23,8 +23,8 @@ class LockChangeHandler : AnimatorChangeHandler {
val viewAnimators = ArrayList<Animator>()
if (!isPush && from != null) {
viewAnimators.add(ObjectAnimator.ofFloat(from, View.SCALE_X, 5f))
viewAnimators.add(ObjectAnimator.ofFloat(from, View.SCALE_Y, 5f))
viewAnimators.add(ObjectAnimator.ofFloat(from, View.SCALE_X, 3f))
viewAnimators.add(ObjectAnimator.ofFloat(from, View.SCALE_Y, 3f))
viewAnimators.add(ObjectAnimator.ofFloat(from, View.ALPHA, 0f))
}

View File

@@ -1,19 +1,35 @@
package exh.ui.lock
import android.annotation.SuppressLint
import android.os.Bundle
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.CardView
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.afollestad.materialdialogs.MaterialDialog
import com.andrognito.pinlockview.PinLockListener
import com.github.ajalt.reprint.core.AuthenticationResult
import com.github.ajalt.reprint.core.Reprint
import com.github.ajalt.reprint.rxjava.RxReprint
import com.mattprecious.swirl.SwirlView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.main.MainActivity
import exh.util.dpToPx
import kotlinx.android.synthetic.main.activity_lock.view.*
import kotlinx.android.synthetic.main.main_activity.view.*
import uy.kohesive.injekt.injectLazy
class LockController : NucleusController<LockPresenter>() {
val prefs: PreferencesHelper by injectLazy()
override fun inflateView(inflater: LayoutInflater, container: ViewGroup)
= inflater.inflate(R.layout.activity_lock, container, false)!!
@@ -21,8 +37,6 @@ class LockController : NucleusController<LockPresenter>() {
override fun getTitle() = "Application locked"
val prefs: PreferencesHelper by injectLazy()
override fun onViewCreated(view: View, savedViewState: Bundle?) {
super.onViewCreated(view, savedViewState)
@@ -32,6 +46,7 @@ class LockController : NucleusController<LockPresenter>() {
}
with(view) {
//Setup pin lock
pin_lock_view.attachIndicatorDots(indicator_dots)
pin_lock_view.pinLength = prefs.lockLength().getOrDefault()
@@ -60,6 +75,61 @@ class LockController : NucleusController<LockPresenter>() {
}
}
@SuppressLint("NewApi")
override fun onAttach(view: View) {
super.onAttach(view)
with(view) {
//Fingerprint
if (presenter.useFingerprint) {
swirl_container.removeAllViews()
val icon = SwirlView(context).apply {
val size = dpToPx(context, 60)
layoutParams = (layoutParams ?: ViewGroup.LayoutParams(
size, size
)).apply {
width = size
height = size
val pSize = dpToPx(context, 8)
setPadding(pSize, pSize, pSize, pSize)
}
val typedVal = TypedValue()
activity!!.theme!!.resolveAttribute(android.R.attr.windowBackground, typedVal, true)
setBackgroundColor(typedVal.data)
//Disable elevation if dark theme is active
if (typedVal.data == resources.getColor(R.color.backgroundDark, activity!!.theme!!))
this@with.swirl_container.cardElevation = 0f
setState(SwirlView.State.OFF, false)
}
swirl_container.addView(icon)
icon.setState(SwirlView.State.ON)
RxReprint.authenticate()
.subscribeUntilDetach {
when (it.status) {
AuthenticationResult.Status.SUCCESS -> closeLock()
AuthenticationResult.Status.NONFATAL_FAILURE -> icon.setState(SwirlView.State.ERROR)
AuthenticationResult.Status.FATAL_FAILURE, null -> {
MaterialDialog.Builder(context)
.title("Fingerprint error!")
.content(it.errorMessage)
.cancelable(false)
.canceledOnTouchOutside(false)
.positiveText("Ok")
.autoDismiss(true)
.show()
icon.setState(SwirlView.State.OFF)
}
}
}
}
}
}
override fun onDetach(view: View) {
super.onDetach(view)
}
fun closeLock() {
router.popCurrentController()
}

View File

@@ -2,11 +2,14 @@ package exh.ui.lock
import android.content.Context
import android.support.v7.preference.Preference
import android.support.v7.preference.SwitchPreference
import android.support.v7.preference.SwitchPreferenceCompat
import android.text.InputType
import android.util.AttributeSet
import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.setting.onChange
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
@@ -15,7 +18,7 @@ import java.math.BigInteger
import java.security.SecureRandom
class LockPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
Preference(context, attrs) {
SwitchPreferenceCompat(context, attrs) {
private val secureRandom by lazy { SecureRandom() }
@@ -24,17 +27,24 @@ class LockPreference @JvmOverloads constructor(context: Context, attrs: Attribut
override fun onAttached() {
super.onAttached()
updateSummary()
onChange {
tryChange()
false
}
}
private fun updateSummary() {
summary = if(lockEnabled(prefs))
"Application is locked"
else
"Application is not locked, tap to lock"
isChecked = lockEnabled(prefs)
if(isChecked) {
title = "Lock enabled"
summary = "Tap to disable or change pin code"
} else {
title = "Lock disabled"
summary = "Tap to enable"
}
}
override fun onClick() {
super.onClick()
fun tryChange() {
if(!notifyLockSecurity(context)) {
MaterialDialog.Builder(context)
.title("Lock application")

View File

@@ -1,6 +1,19 @@
package exh.ui.lock
import android.os.Build
import com.github.ajalt.reprint.core.Reprint
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import uy.kohesive.injekt.injectLazy
class LockPresenter: BasePresenter<LockController>()
class LockPresenter: BasePresenter<LockController>() {
val prefs: PreferencesHelper by injectLazy()
val useFingerprint
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& Reprint.isHardwarePresent()
&& Reprint.hasFingerprintRegistered()
&& prefs.lockUseFingerprint().getOrDefault()
}