Skip to content

Commit

Permalink
Fingerprint/Biometric Unlock Support
Browse files Browse the repository at this point in the history
Version 0.9.3 set
  • Loading branch information
Jays2Kings committed Dec 18, 2019
1 parent 5448a16 commit 51e735a
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 17 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Tachiyomi is a free and open source manga reader for Android.
![screenshots of app](./.github/readme-images/theming-screenshots.gif)

## Newest Release
[v0.9.2](https://github.com/Jays2Kings/tachiyomi/releases)
[v0.9.3](https://github.com/Jays2Kings/tachiyomi/releases)

## Features

Expand Down
13 changes: 9 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ android {
minSdkVersion 21
targetSdkVersion 29
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 44
versionName '0.9.2'
versionCode 45
versionName '0.9.3'

buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
Expand Down Expand Up @@ -115,17 +115,22 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'androidx.browser:browser:1.0.0'
implementation 'androidx.biometric:biometric:1.0.0'

implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

implementation 'androidx.multidex:multidex:2.0.1'

standardImplementation 'com.google.firebase:firebase-core:17.2.1'

final lifecycle_version = "2.1.0"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

// ReactiveX
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'io.reactivex:rxjava:1.3.8'
Expand Down Expand Up @@ -245,7 +250,7 @@ dependencies {
}

buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.3.61'
repositories {
mavenCentral()
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
<activity
android:name=".ui.manga.info.WebViewActivity"
android:configChanges="uiMode|orientation|screenSize"/>
<activity
android:name=".ui.main.BiometricActivity" />
<activity
android:name=".widget.CustomLayoutPickerActivity"
android:label="@string/app_name"
Expand Down
20 changes: 19 additions & 1 deletion app/src/main/java/eu/kanade/tachiyomi/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@ package eu.kanade.tachiyomi
import android.app.Application
import android.content.Context
import android.content.res.Configuration
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.multidex.MultiDex
import com.evernote.android.job.JobManager
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.updater.UpdaterJob
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.LocaleHelper
import org.acra.ACRA
import org.acra.annotation.ReportsCrashes
import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.InjektScope
import uy.kohesive.injekt.injectLazy
import uy.kohesive.injekt.registry.default.DefaultRegistrar

@ReportsCrashes(
Expand All @@ -24,7 +32,7 @@ import uy.kohesive.injekt.registry.default.DefaultRegistrar
buildConfigClass = BuildConfig::class,
excludeMatchingSharedPreferencesKeys = arrayOf(".*username.*", ".*password.*", ".*token.*")
)
open class App : Application() {
open class App : Application(), LifecycleObserver {

override fun onCreate() {
super.onCreate()
Expand All @@ -38,6 +46,16 @@ open class App : Application() {
setupNotificationChannels()

LocaleHelper.updateConfiguration(this, resources.configuration)
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
//App in background
val preferences: PreferencesHelper by injectLazy()
if (preferences.lockAfter().getOrDefault() >= 0) {
MainActivity.unlocked = false
}
}

override fun attachBaseContext(base: Context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ object PreferenceKeys {

const val downloadBadge = "display_download_badge"

const val useBiometrics = "use_biometrics"

const val lockAfter = "lock_after"

const val lastUnlock = "last_unlock"

@Deprecated("Use the preferences of the source")
fun sourceUsername(sourceId: Long) = "pref_source_username_$sourceId"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ class PreferencesHelper(val context: Context) {

fun skipRead() = prefs.getBoolean(Keys.skipRead, false)

fun useBiometrics() = rxPrefs.getBoolean(Keys.useBiometrics, false)

fun lockAfter() = rxPrefs.getInteger(Keys.lockAfter, 0)

fun lastUnlock() = rxPrefs.getLong(Keys.lastUnlock, 0)

fun migrateFlags() = rxPrefs.getInteger("migrate_flags", Int.MAX_VALUE)

fun trustedSignatures() = rxPrefs.getStringSet("trusted_signatures", emptySet())
Expand Down
47 changes: 47 additions & 0 deletions app/src/main/java/eu/kanade/tachiyomi/ui/main/BiometricActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package eu.kanade.tachiyomi.ui.main

import android.os.Bundle
import androidx.biometric.BiometricPrompt
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import uy.kohesive.injekt.injectLazy
import java.util.Date
import java.util.concurrent.Executors

class BiometricActivity : BaseActivity() {
val executor = Executors.newSingleThreadExecutor()

val preferences: PreferencesHelper by injectLazy()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt
.AuthenticationCallback() {

override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
finishAffinity()
}

override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
MainActivity.unlocked = true
preferences.lastUnlock().set(Date().time)
finish()
}

override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
// TODO("Called when a biometric is valid but not recognized.")
}
})

val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(getString(R.string.unlock_library))
.setNegativeButtonText(getString(android.R.string.cancel))
.build()

biometricPrompt.authenticate(promptInfo)
}

}
23 changes: 22 additions & 1 deletion app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.biometric.BiometricManager
import androidx.core.graphics.ColorUtils
import com.bluelinelabs.conductor.*
import com.google.android.material.snackbar.Snackbar
import eu.kanade.tachiyomi.Migrations
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import eu.kanade.tachiyomi.ui.base.controller.*
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
Expand All @@ -46,6 +48,7 @@ import eu.kanade.tachiyomi.util.updatePaddingRelative
import kotlinx.android.synthetic.main.main_activity.*
import kotlinx.coroutines.delay
import uy.kohesive.injekt.injectLazy
import java.util.Date

class MainActivity : BaseActivity() {

Expand All @@ -60,7 +63,6 @@ class MainActivity : BaseActivity() {
private var snackBar:Snackbar? = null
var extraViewForUndo:View? = null
private var canDismissSnackBar = false

fun setUndoSnackBar(snackBar: Snackbar?, extraViewToCheck: View? = null) {
this.snackBar = snackBar
canDismissSnackBar = false
Expand Down Expand Up @@ -248,6 +250,22 @@ class MainActivity : BaseActivity() {
}
}

override fun onResume() {
super.onResume()
val useBiometrics = preferences.useBiometrics().getOrDefault()
if (useBiometrics && BiometricManager.from(this)
.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
if (!unlocked && (preferences.lockAfter().getOrDefault() <= 0 || Date().time >=
preferences.lastUnlock().getOrDefault() + 60 * 1000 * preferences.lockAfter().getOrDefault())) {
val intent = Intent(this, BiometricActivity::class.java)
startActivity(intent)
this.overridePendingTransition(0, 0)
}
}
else if (useBiometrics)
preferences.useBiometrics().set(false)
}

override fun onNewIntent(intent: Intent) {
if (!handleIntentAction(intent)) {
super.onNewIntent(intent)
Expand Down Expand Up @@ -314,6 +332,7 @@ class MainActivity : BaseActivity() {
} else if (backstackSize == 1 && router.getControllerWithTag("$startScreenId") == null) {
setSelectedDrawerItem(startScreenId)
} else if (backstackSize == 1 || !router.handleBack()) {
unlocked = false
super.onBackPressed()
}
}
Expand Down Expand Up @@ -412,6 +431,8 @@ class MainActivity : BaseActivity() {
const val INTENT_SEARCH_FILTER = "filter"

private const val URL_HELP = "https://tachiyomi.org/help/"

var unlocked = false
}

}
43 changes: 36 additions & 7 deletions app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,28 @@ import android.graphics.Bitmap
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import com.google.android.material.bottomsheet.BottomSheetDialog
import android.view.*
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.LinearLayout
import android.widget.SeekBar
import androidx.biometric.BiometricManager
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.bottomsheet.BottomSheetDialog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
import eu.kanade.tachiyomi.ui.main.BiometricActivity
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.AddToLibraryFirst
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Error
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Success
Expand All @@ -38,12 +44,17 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.L2RPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import eu.kanade.tachiyomi.util.*
import eu.kanade.tachiyomi.util.GLUtil
import eu.kanade.tachiyomi.util.getResourceColor
import eu.kanade.tachiyomi.util.getUriCompat
import eu.kanade.tachiyomi.util.gone
import eu.kanade.tachiyomi.util.launchUI
import eu.kanade.tachiyomi.util.plusAssign
import eu.kanade.tachiyomi.util.toast
import eu.kanade.tachiyomi.util.visible
import eu.kanade.tachiyomi.widget.SimpleAnimationListener
import eu.kanade.tachiyomi.widget.SimpleSeekBarListener
import kotlinx.android.synthetic.main.reader_activity.*
import kotlinx.android.synthetic.main.reader_activity.toolbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import me.zhanghai.android.systemuihelper.SystemUiHelper
Expand All @@ -55,6 +66,7 @@ import rx.subscriptions.CompositeSubscription
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.util.Date
import java.util.concurrent.TimeUnit

/**
Expand Down Expand Up @@ -506,6 +518,23 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
presenter.shareImage(page)
}

override fun onResume() {
super.onResume()
val useBiometrics = preferences.useBiometrics().getOrDefault()
if (useBiometrics && BiometricManager.from(this)
.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
if (!MainActivity.unlocked && (preferences.lockAfter().getOrDefault() <= 0 || Date()
.time >=
preferences.lastUnlock().getOrDefault() + 60 * 1000 * preferences.lockAfter().getOrDefault())) {
val intent = Intent(this, BiometricActivity::class.java)
startActivity(intent)
this.overridePendingTransition(0, 0)
}
}
else if (useBiometrics)
preferences.useBiometrics().set(false)
}

/**
* Called from the presenter when a page is ready to be shared. It shows Android's default
* sharing tool.
Expand Down
Loading

0 comments on commit 51e735a

Please sign in to comment.