From b20b3d6f5c6c965876de4409fd79fec5f9d5543e Mon Sep 17 00:00:00 2001 From: NerdNumber9 <nerd.number9@yandex.com> Date: Thu, 24 Aug 2017 17:11:43 -0400 Subject: [PATCH] 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 --- app/build.gradle | 9 +- app/src/main/AndroidManifest.xml | 3 +- app/src/main/java/eu/kanade/tachiyomi/App.kt | 2 + .../data/preference/PreferencesHelper.kt | 2 + .../tachiyomi/source/online/all/EHentai.kt | 8 +- .../kanade/tachiyomi/ui/main/MainActivity.kt | 25 ++- .../ui/setting/SettingsEhController.kt | 16 +- .../ui/setting/SettingsGeneralController.kt | 23 ++- .../exh/ui/batchadd/BatchAddController.kt | 9 +- .../java/exh/ui/batchadd/BatchAddPresenter.kt | 14 +- .../java/exh/ui/lock/FingerLockPreference.kt | 149 ++++++++++++++++++ .../java/exh/ui/lock/LockChangeHandler.kt | 4 +- .../main/java/exh/ui/lock/LockController.kt | 74 ++++++++- .../main/java/exh/ui/lock/LockPreference.kt | 24 ++- .../main/java/exh/ui/lock/LockPresenter.kt | 15 +- .../{LoginActivity.kt => LoginController.kt} | 109 +++++++------ .../main/java/exh/ui/login/LoginPresenter.kt | 7 + app/src/main/java/exh/util/ViewUtil.kt | 8 + app/src/main/res/layout/activity_lock.xml | 16 ++ .../main/res/layout/eh_fragment_batch_add.xml | 26 +-- app/src/main/res/values/themes.xml | 4 + 21 files changed, 438 insertions(+), 109 deletions(-) create mode 100644 app/src/main/java/exh/ui/lock/FingerLockPreference.kt rename app/src/main/java/exh/ui/login/{LoginActivity.kt => LoginController.kt} (66%) create mode 100644 app/src/main/java/exh/ui/login/LoginPresenter.kt create mode 100644 app/src/main/java/exh/util/ViewUtil.kt diff --git a/app/build.gradle b/app/build.gradle index 058b0e089..5dd61f9d4 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -219,9 +219,16 @@ dependencies { //JVE (Regex) (EH) compile 'ru.lanwen.verbalregex:java-verbal-expressions:1.4' - //Pin lock view + //Pin lock view (EXH) compile 'com.andrognito.pinlockview:pinlockview:1.0.1' + //Reprint (EXH) + compile 'com.github.ajalt.reprint:core:3.1.0@aar' // required: supports marshmallow devices + compile 'com.github.ajalt.reprint:rxjava:3.1.0@aar' // optional: the RxJava 1 interface + + //Swirl (EXH) + compile 'com.mattprecious.swirl:swirl:1.0.0' + // Tests //Paper DB screws up tests /*testCompile 'junit:junit:4.12' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5762deea3..2447e6800 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" /> + <uses-permission android:name="android.permission.USE_FINGERPRINT" /> <uses-permission android:name="android.permission.GET_TASKS"/> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" /> @@ -106,7 +107,7 @@ <!-- EH --> <activity - android:name="exh.ui.login.LoginActivity" + android:name="exh.ui.login.LoginController" android:label="@string/label_login"> <!-- TODO parent activity --> </activity> diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index af89227b6..4997f2587 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.res.Configuration import android.support.multidex.MultiDex import com.evernote.android.job.JobManager +import com.github.ajalt.reprint.core.Reprint import eu.kanade.tachiyomi.data.backup.BackupCreatorJob import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.updater.UpdateCheckerJob @@ -26,6 +27,7 @@ open class App : Application() { setupJobManager() Paper.init(this) //Setup metadata DB (EH) + Reprint.initialize(this) //Setup fingerprint (EH) LocaleHelper.updateConfiguration(this, resources.configuration) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 46cedc1bf..586a4c0c4 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -199,5 +199,7 @@ class PreferencesHelper(val context: Context) { fun lockSalt() = rxPrefs.getString("lock_salt", null) fun lockLength() = rxPrefs.getInteger("lock_length", -1) + + fun lockUseFingerprint() = rxPrefs.getBoolean("lock_finger", false) // <-- EH } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt index 0217a417c..53077e13b 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt @@ -19,7 +19,7 @@ import rx.Observable import uy.kohesive.injekt.injectLazy import java.net.URLEncoder import java.util.* -import exh.ui.login.LoginActivity +import exh.ui.login.LoginController import exh.util.UriFilter import exh.util.UriGroup import okhttp3.CacheControl @@ -323,9 +323,9 @@ class EHentai(override val id: Long, val cookiesHeader by lazy { val cookies: MutableMap<String, String> = mutableMapOf() if(prefs.enableExhentai().getOrDefault()) { - cookies.put(LoginActivity.MEMBER_ID_COOKIE, prefs.memberIdVal().get()!!) - cookies.put(LoginActivity.PASS_HASH_COOKIE, prefs.passHashVal().get()!!) - cookies.put(LoginActivity.IGNEOUS_COOKIE, prefs.igneousVal().get()!!) + cookies.put(LoginController.MEMBER_ID_COOKIE, prefs.memberIdVal().get()!!) + cookies.put(LoginController.PASS_HASH_COOKIE, prefs.passHashVal().get()!!) + cookies.put(LoginController.IGNEOUS_COOKIE, prefs.igneousVal().get()!!) } //Setup settings diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 2adeadc5f..642ab27bf 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.main import android.animation.ObjectAnimator import android.app.ActivityManager import android.app.Service -import android.app.usage.UsageStats import android.app.usage.UsageStatsManager import android.content.Intent import android.graphics.Color @@ -15,8 +14,6 @@ import android.support.v7.graphics.drawable.DrawerArrowDrawable import android.view.ViewGroup import com.bluelinelabs.conductor.* import com.bluelinelabs.conductor.changehandler.FadeChangeHandler -import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler -import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler import eu.kanade.tachiyomi.Migrations import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper @@ -40,7 +37,6 @@ import exh.ui.lock.lockEnabled import exh.ui.lock.notifyLockSecurity import kotlinx.android.synthetic.main.main_activity.* import uy.kohesive.injekt.injectLazy -import java.util.* class MainActivity : BaseActivity() { @@ -49,7 +45,7 @@ class MainActivity : BaseActivity() { val preferences: PreferencesHelper by injectLazy() - private var drawerArrow: DrawerArrowDrawable? = null + var drawerArrow: DrawerArrowDrawable? = null private var secondaryDrawer: ViewGroup? = null @@ -153,6 +149,8 @@ class MainActivity : BaseActivity() { if (savedInstanceState == null) { val lockEnabled = lockEnabled(preferences) if (lockEnabled) { + //Special case first lock + toolbar.navigationIcon = null doLock() //Check lock security @@ -236,6 +234,16 @@ class MainActivity : BaseActivity() { drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) } + // --> EH + //Special case and hide drawer arrow for lock controller + if(to is LockController) { + supportActionBar?.setDisplayHomeAsUpEnabled(false) + } else { + supportActionBar?.setDisplayHomeAsUpEnabled(true) + toolbar.navigationIcon = drawerArrow + } + // <-- EH + ObjectAnimator.ofFloat(drawerArrow, "progress", if (showHamburger) 0f else 1f).start() if (from is TabbedController) { @@ -270,10 +278,9 @@ class MainActivity : BaseActivity() { // --> EH //Lock code var willLock = false - var disableLock = false override fun onRestart() { super.onRestart() - if(willLock && lockEnabled() && !disableLock) { + if(willLock && lockEnabled()) { doLock() } @@ -286,6 +293,10 @@ class MainActivity : BaseActivity() { } fun tryLock() { + //Do not double-lock + if(router.backstack.lastOrNull()?.controller() is LockController) + return + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val mUsageStatsManager = getSystemService("usagestats") as UsageStatsManager val time = System.currentTimeMillis() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt index 650138ce5..74b2a1dee 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt @@ -1,9 +1,11 @@ package eu.kanade.tachiyomi.ui.setting -import android.content.Intent import android.support.v7.preference.PreferenceScreen +import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import exh.ui.migration.MetadataFetchDialog -import exh.ui.login.LoginActivity +import exh.ui.login.LoginController +import rx.android.schedulers.AndroidSchedulers /** * EH Settings fragment @@ -20,9 +22,11 @@ class SettingsEhController : SettingsController() { isPersistent = false defaultValue = false preferences.enableExhentai() - .asObservable().subscribeUntilDestroy { + .asObservable() + .observeOn(AndroidSchedulers.mainThread()) + .subscribeUntilDestroy { isChecked = it - } + } onChange { newVal -> newVal as Boolean @@ -30,7 +34,9 @@ class SettingsEhController : SettingsController() { preferences.enableExhentai().set(false) true } else { - startActivity(Intent(context, LoginActivity::class.java)) + router.pushController(RouterTransaction.with(LoginController()) + .pushChangeHandler(FadeChangeHandler()) + .popChangeHandler(FadeChangeHandler())) false } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt index ee2a241f9..1ac99c63c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt @@ -4,6 +4,7 @@ import android.app.Dialog import android.os.Bundle import android.os.Handler import android.support.v7.preference.PreferenceScreen +import android.support.v7.preference.SwitchPreference import android.view.View import com.afollestad.materialdialogs.MaterialDialog import eu.kanade.tachiyomi.R @@ -13,6 +14,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.util.LocaleHelper +import exh.ui.lock.FingerLockPreference import exh.ui.lock.LockPreference import kotlinx.android.synthetic.main.pref_library_columns.view.* import rx.Observable @@ -176,12 +178,25 @@ class SettingsGeneralController : SettingsController() { true } } - LockPreference(context).apply { - key = "pref_app_lock" + preferenceCategory { title = "Application lock" - isPersistent = false - addPreference(this) + LockPreference(context).apply { + key = "pref_app_lock" + isPersistent = false + + addPreference(this) + } + + FingerLockPreference(context).apply { + key = "pref_lock_finger" + isPersistent = false + + addPreference(this) + + //Call after addPreference + dependency = "pref_app_lock" + } } } diff --git a/app/src/main/java/exh/ui/batchadd/BatchAddController.kt b/app/src/main/java/exh/ui/batchadd/BatchAddController.kt index 401cd9120..bad59e8e2 100755 --- a/app/src/main/java/exh/ui/batchadd/BatchAddController.kt +++ b/app/src/main/java/exh/ui/batchadd/BatchAddController.kt @@ -35,7 +35,7 @@ class BatchAddController : NucleusController<BatchAddPresenter>() { } progress_dismiss_btn.clicks().subscribeUntilDestroy { - presenter.currentlyAddingRelay.call(false) + presenter.currentlyAddingRelay.call(BatchAddPresenter.STATE_PROGRESS_TO_INPUT) } val progressSubscriptions = CompositeSubscription() @@ -44,7 +44,7 @@ class BatchAddController : NucleusController<BatchAddPresenter>() { .observeOn(AndroidSchedulers.mainThread()) .subscribeUntilDestroy { progressSubscriptions.clear() - if(it) { + if(it == BatchAddPresenter.STATE_INPUT_TO_PROGRESS) { showProgress(this) progressSubscriptions += presenter.progressRelay .observeOn(AndroidSchedulers.mainThread()) @@ -79,7 +79,10 @@ class BatchAddController : NucleusController<BatchAddPresenter>() { }?.let { progressSubscriptions += it } - } else hideProgress(this) + } else if(it == BatchAddPresenter.STATE_PROGRESS_TO_INPUT) { + hideProgress(this) + presenter.currentlyAddingRelay.call(BatchAddPresenter.STATE_IDLE) + } } } } diff --git a/app/src/main/java/exh/ui/batchadd/BatchAddPresenter.kt b/app/src/main/java/exh/ui/batchadd/BatchAddPresenter.kt index 40dffcb15..5e0a05076 100644 --- a/app/src/main/java/exh/ui/batchadd/BatchAddPresenter.kt +++ b/app/src/main/java/exh/ui/batchadd/BatchAddPresenter.kt @@ -15,18 +15,18 @@ class BatchAddPresenter: BasePresenter<BatchAddController>() { val progressTotalRelay = BehaviorRelay.create(0)!! val progressRelay = BehaviorRelay.create(0)!! var eventRelay: ReplayRelay<String>? = null - val currentlyAddingRelay = BehaviorRelay.create(false)!! + val currentlyAddingRelay = BehaviorRelay.create(STATE_IDLE)!! fun addGalleries(galleries: String) { eventRelay = ReplayRelay.create() - val splitGalleries = galleries.split("\n").map { + val splitGalleries = galleries.split("\n").mapNotNull { it.trim().nullIfBlank() - }.filterNotNull() + } progressRelay.call(0) progressTotalRelay.call(splitGalleries.size) - currentlyAddingRelay.call(true) + currentlyAddingRelay.call(STATE_INPUT_TO_PROGRESS) thread { val succeeded = mutableListOf<String>() @@ -48,4 +48,10 @@ class BatchAddPresenter: BasePresenter<BatchAddController>() { eventRelay?.call(summary) } } + + companion object { + const val STATE_IDLE = 0 + const val STATE_INPUT_TO_PROGRESS = 1 + const val STATE_PROGRESS_TO_INPUT = 2 + } } diff --git a/app/src/main/java/exh/ui/lock/FingerLockPreference.kt b/app/src/main/java/exh/ui/lock/FingerLockPreference.kt new file mode 100644 index 000000000..6a29e2a7f --- /dev/null +++ b/app/src/main/java/exh/ui/lock/FingerLockPreference.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/exh/ui/lock/LockChangeHandler.kt b/app/src/main/java/exh/ui/lock/LockChangeHandler.kt index e70227396..d72f7db42 100644 --- a/app/src/main/java/exh/ui/lock/LockChangeHandler.kt +++ b/app/src/main/java/exh/ui/lock/LockChangeHandler.kt @@ -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)) } diff --git a/app/src/main/java/exh/ui/lock/LockController.kt b/app/src/main/java/exh/ui/lock/LockController.kt index 59e5ff6b3..4ab9621dd 100755 --- a/app/src/main/java/exh/ui/lock/LockController.kt +++ b/app/src/main/java/exh/ui/lock/LockController.kt @@ -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() } diff --git a/app/src/main/java/exh/ui/lock/LockPreference.kt b/app/src/main/java/exh/ui/lock/LockPreference.kt index 8985d3e21..824b2376e 100755 --- a/app/src/main/java/exh/ui/lock/LockPreference.kt +++ b/app/src/main/java/exh/ui/lock/LockPreference.kt @@ -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") diff --git a/app/src/main/java/exh/ui/lock/LockPresenter.kt b/app/src/main/java/exh/ui/lock/LockPresenter.kt index 22c02d31c..5941e6f12 100644 --- a/app/src/main/java/exh/ui/lock/LockPresenter.kt +++ b/app/src/main/java/exh/ui/lock/LockPresenter.kt @@ -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() +} diff --git a/app/src/main/java/exh/ui/login/LoginActivity.kt b/app/src/main/java/exh/ui/login/LoginController.kt similarity index 66% rename from app/src/main/java/exh/ui/login/LoginActivity.kt rename to app/src/main/java/exh/ui/login/LoginController.kt index a997969fb..7b495fb63 100755 --- a/app/src/main/java/exh/ui/login/LoginActivity.kt +++ b/app/src/main/java/exh/ui/login/LoginController.kt @@ -3,7 +3,9 @@ package exh.ui.login import android.net.Uri import android.os.Build import android.os.Bundle -import android.view.MenuItem +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import android.webkit.CookieManager import android.webkit.WebView import android.webkit.WebViewClient @@ -12,9 +14,9 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.all.EHentai -import eu.kanade.tachiyomi.ui.base.activity.BaseActivity +import eu.kanade.tachiyomi.ui.base.controller.NucleusController import exh.EXH_SOURCE_ID -import kotlinx.android.synthetic.main.eh_activity_login.* +import kotlinx.android.synthetic.main.eh_activity_login.view.* import rx.Observable import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers @@ -23,70 +25,75 @@ import uy.kohesive.injekt.injectLazy import java.net.HttpCookie /** - * LoginActivity + * LoginController */ -class LoginActivity : BaseActivity() { - +class LoginController : NucleusController<LoginPresenter>() { val preferenceManager: PreferencesHelper by injectLazy() val sourceManager: SourceManager by injectLazy() - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.eh_activity_login) + override fun getTitle() = "ExHentai login" - setup() - } + override fun createPresenter() = LoginPresenter() - fun setup() { - btn_cancel.setOnClickListener { onBackPressed() } - btn_recheck.setOnClickListener { webview.loadUrl("http://exhentai.org/") } + override fun inflateView(inflater: LayoutInflater, container: ViewGroup) = + inflater.inflate(R.layout.eh_activity_login, container, false)!! - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - CookieManager.getInstance().removeAllCookies { - runOnUiThread { - startWebview() + override fun onViewCreated(view: View, savedViewState: Bundle?) { + super.onViewCreated(view, savedViewState) + + with(view) { + btn_cancel.setOnClickListener { router.popCurrentController() } + btn_recheck.setOnClickListener { webview.loadUrl("http://exhentai.org/") } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + CookieManager.getInstance().removeAllCookies { + Observable.fromCallable { + startWebview(view) + }.subscribeOn(AndroidSchedulers.mainThread()).subscribe() } + } else { + CookieManager.getInstance().removeAllCookie() + startWebview(view) } - } else { - CookieManager.getInstance().removeAllCookie() - startWebview() } } - fun startWebview() { - webview.settings.javaScriptEnabled = true - webview.settings.domStorageEnabled = true + fun startWebview(view: View) { + with(view) { + webview.settings.javaScriptEnabled = true + webview.settings.domStorageEnabled = true - webview.loadUrl("https://forums.e-hentai.org/index.php?act=Login") + webview.loadUrl("https://forums.e-hentai.org/index.php?act=Login") - webview.setWebViewClient(object : WebViewClient() { - override fun onPageFinished(view: WebView, url: String) { - super.onPageFinished(view, url) - Timber.d(url) - val parsedUrl = Uri.parse(url) - if(parsedUrl.host.equals("forums.e-hentai.org", ignoreCase = true)) { - //Hide distracting content - view.loadUrl(HIDE_JS) + webview.setWebViewClient(object : WebViewClient() { + override fun onPageFinished(view: WebView, url: String) { + super.onPageFinished(view, url) + Timber.d(url) + val parsedUrl = Uri.parse(url) + if (parsedUrl.host.equals("forums.e-hentai.org", ignoreCase = true)) { + //Hide distracting content + view.loadUrl(HIDE_JS) - //Check login result - if(parsedUrl.getQueryParameter("code")?.toInt() != 0) { - if(checkLoginCookies(url)) view.loadUrl("http://exhentai.org/") - } - } else if(parsedUrl.host.equals("exhentai.org", ignoreCase = true)) { - //At ExHentai, check that everything worked out... - if(applyExHentaiCookies(url)) { - preferenceManager.enableExhentai().set(true) - finishLogin() + //Check login result + if (parsedUrl.getQueryParameter("code")?.toInt() != 0) { + if (checkLoginCookies(url)) view.loadUrl("http://exhentai.org/") + } + } else if (parsedUrl.host.equals("exhentai.org", ignoreCase = true)) { + //At ExHentai, check that everything worked out... + if (applyExHentaiCookies(url)) { + preferenceManager.enableExhentai().set(true) + finishLogin(view) + } } } - } - }) + }) + } } - fun finishLogin() { - val progressDialog = MaterialDialog.Builder(this) + fun finishLogin(view: View) { + val progressDialog = MaterialDialog.Builder(view.context) .title("Finalizing login") .progress(true, 0) .content("Please wait...") @@ -108,7 +115,7 @@ class LoginActivity : BaseActivity() { .observeOn(AndroidSchedulers.mainThread()) .subscribe { progressDialog.dismiss() - onBackPressed() + router.popCurrentController() } } @@ -164,14 +171,6 @@ class LoginActivity : BaseActivity() { } } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> onBackPressed() - else -> return super.onOptionsItemSelected(item) - } - return true - } - companion object { const val MEMBER_ID_COOKIE = "ipb_member_id" const val PASS_HASH_COOKIE = "ipb_pass_hash" diff --git a/app/src/main/java/exh/ui/login/LoginPresenter.kt b/app/src/main/java/exh/ui/login/LoginPresenter.kt new file mode 100644 index 000000000..3d39afcc5 --- /dev/null +++ b/app/src/main/java/exh/ui/login/LoginPresenter.kt @@ -0,0 +1,7 @@ +package exh.ui.login + +import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter + +class LoginPresenter: BasePresenter<LoginController>() { + +} \ No newline at end of file diff --git a/app/src/main/java/exh/util/ViewUtil.kt b/app/src/main/java/exh/util/ViewUtil.kt new file mode 100644 index 000000000..edd35d1a9 --- /dev/null +++ b/app/src/main/java/exh/util/ViewUtil.kt @@ -0,0 +1,8 @@ +package exh.util + +import android.content.Context + +fun dpToPx(context: Context, dp: Int): Int { + val scale = context.resources.displayMetrics.density + return (dp * scale + 0.5f).toInt() +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_lock.xml b/app/src/main/res/layout/activity_lock.xml index 7005ce1ba..ad4ac7150 100755 --- a/app/src/main/res/layout/activity_lock.xml +++ b/app/src/main/res/layout/activity_lock.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + xmlns:card_view="http://schemas.android.com/apk/res-auto" android:background="@color/backgroundDark"> <com.andrognito.pinlockview.PinLockView @@ -26,4 +27,19 @@ app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="packed" /> + + <android.support.v7.widget.CardView + android:id="@+id/swirl_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + card_view:cardCornerRadius="30dp" + card_view:cardElevation="4dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent"> + + </android.support.v7.widget.CardView> </android.support.constraint.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/eh_fragment_batch_add.xml b/app/src/main/res/layout/eh_fragment_batch_add.xml index 03faf0ec6..a4001371a 100755 --- a/app/src/main/res/layout/eh_fragment_batch_add.xml +++ b/app/src/main/res/layout/eh_fragment_batch_add.xml @@ -11,7 +11,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:animateLayoutChanges="true" - android:padding="16dp "> + android:padding="16dp"> <TextView android:id="@+id/input_title_view" @@ -19,7 +19,7 @@ android:layout_height="wrap_content" android:text="Enter the galleries to add (separated by a new line):" android:textAppearance="@style/TextAppearance.Medium.Title" - android:visibility="gone" + android:visibility="visible" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -31,7 +31,7 @@ android:ems="10" android:hint="Example:\n\nhttp://e-hentai.org/g/12345/1a2b3c4e\nhttp://g.e-hentai.org/g/67890/6f7g8h9i\nhttp://exhentai.org/g/13579/1a3b5c7e\nhttps://exhentai.org/g/24680/2f4g6h8i\n" android:inputType="textMultiLine" - android:visibility="gone" + android:visibility="visible" app:layout_constraintBottom_toTopOf="@+id/btn_add_galleries" app:layout_constraintLeft_toLeftOf="@+id/input_title_view" app:layout_constraintRight_toRightOf="@+id/input_title_view" @@ -42,7 +42,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:text="Add Galleries" - android:visibility="gone" + android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="@+id/galleries_box" app:layout_constraintRight_toRightOf="@+id/galleries_box" /> @@ -58,7 +58,7 @@ android:layout_marginTop="0dp" android:text="Adding galleries..." android:textAppearance="@style/TextAppearance.Medium.Title" - android:visibility="visible" + android:visibility="gone" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -71,7 +71,7 @@ android:layout_marginBottom="0dp" android:layout_marginEnd="8dp" android:layout_marginRight="8dp" - android:visibility="visible" + android:visibility="gone" app:layout_constraintBottom_toTopOf="@+id/progress_dismiss_btn" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintLeft_toLeftOf="@+id/progress_log_wrapper" @@ -85,7 +85,7 @@ android:singleLine="true" android:text="" android:textAlignment="textEnd" - android:visibility="visible" + android:visibility="gone" app:layout_constraintRight_toRightOf="@+id/progress_log_wrapper" app:layout_constraintTop_toTopOf="@+id/progress_bar" /> @@ -93,15 +93,15 @@ android:id="@+id/progress_dismiss_btn" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginBottom="0dp" android:layout_marginLeft="0dp" android:layout_marginRight="0dp" android:text="Finish" - android:visibility="visible" - app:layout_constraintLeft_toLeftOf="@+id/progress_log_wrapper" - app:layout_constraintRight_toRightOf="@+id/progress_log_wrapper" - app:layout_constraintHorizontal_bias="0.0" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="@+id/btn_add_galleries" - android:layout_marginBottom="0dp" /> + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintLeft_toLeftOf="@+id/progress_log_wrapper" + app:layout_constraintRight_toRightOf="@+id/progress_log_wrapper" /> <ScrollView android:id="@+id/progress_log_wrapper" @@ -109,7 +109,7 @@ android:layout_height="0dp" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" - android:visibility="visible" + android:visibility="gone" app:layout_constraintBottom_toTopOf="@+id/progress_bar" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintLeft_toLeftOf="@+id/progress_title_view" diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 8a82972a3..e1faa2590 100755 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -24,6 +24,10 @@ <item name="android:divider">@color/dividerLight</item> <item name="android:listDivider">@drawable/line_divider_light</item> + <!-- Swirl (EH) --> + <item name="swirl_ridgeColor">?android:attr/textColorSecondary</item> + <item name="swirl_errorColor">?android:attr/colorAccent</item> + <!-- Themes --> <item name="windowActionModeOverlay">true</item> <item name="actionBarTheme">@style/Theme.ActionBar.Light</item>