Cleanup and moving biometrics stuff + added secure sceen option
Search activity no longer asks for biometrics, but will ask for it to perform certain tasks Co-Authored-By: arkon <arkon@users.noreply.github.com>
This commit is contained in:
parent
bfec83440c
commit
243bffebf9
@ -62,7 +62,7 @@
|
|||||||
android:name=".ui.webview.WebViewActivity"
|
android:name=".ui.webview.WebViewActivity"
|
||||||
android:configChanges="uiMode|orientation|screenSize"/>
|
android:configChanges="uiMode|orientation|screenSize"/>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.main.BiometricActivity" />
|
android:name=".ui.security.BiometricActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".widget.CustomLayoutPickerActivity"
|
android:name=".widget.CustomLayoutPickerActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.data.updater.UpdaterJob
|
import eu.kanade.tachiyomi.data.updater.UpdaterJob
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
|
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import org.acra.ACRA
|
import org.acra.ACRA
|
||||||
import org.acra.annotation.ReportsCrashes
|
import org.acra.annotation.ReportsCrashes
|
||||||
@ -55,7 +55,7 @@ open class App : Application(), LifecycleObserver {
|
|||||||
//App in background
|
//App in background
|
||||||
val preferences: PreferencesHelper by injectLazy()
|
val preferences: PreferencesHelper by injectLazy()
|
||||||
if (preferences.lockAfter().getOrDefault() >= 0) {
|
if (preferences.lockAfter().getOrDefault() >= 0) {
|
||||||
MainActivity.unlocked = false
|
SecureActivityDelegate.locked = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +131,8 @@ object PreferenceKeys {
|
|||||||
|
|
||||||
const val lastUnlock = "last_unlock"
|
const val lastUnlock = "last_unlock"
|
||||||
|
|
||||||
|
const val secureScreen = "secure_screen"
|
||||||
|
|
||||||
const val removeArticles = "remove_articles"
|
const val removeArticles = "remove_articles"
|
||||||
|
|
||||||
const val skipPreMigration = "skip_pre_migration"
|
const val skipPreMigration = "skip_pre_migration"
|
||||||
|
@ -215,6 +215,8 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun lastUnlock() = rxPrefs.getLong(Keys.lastUnlock, 0)
|
fun lastUnlock() = rxPrefs.getLong(Keys.lastUnlock, 0)
|
||||||
|
|
||||||
|
fun secureScreen() = rxPrefs.getBoolean(Keys.secureScreen, false)
|
||||||
|
|
||||||
fun removeArticles() = rxPrefs.getBoolean(Keys.removeArticles, false)
|
fun removeArticles() = rxPrefs.getBoolean(Keys.removeArticles, false)
|
||||||
|
|
||||||
fun migrateFlags() = rxPrefs.getInteger("migrate_flags", Int.MAX_VALUE)
|
fun migrateFlags() = rxPrefs.getInteger("migrate_flags", Int.MAX_VALUE)
|
||||||
|
@ -5,6 +5,9 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
||||||
|
import eu.kanade.tachiyomi.ui.security.BiometricActivity
|
||||||
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
@ -32,6 +35,12 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
else -> R.style.Theme_Tachiyomi
|
else -> R.style.Theme_Tachiyomi
|
||||||
})
|
})
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
SecureActivityDelegate.setSecure(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
if (this !is BiometricActivity && this !is SearchActivity)
|
||||||
|
SecureActivityDelegate.promptLockIfNeeded(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package eu.kanade.tachiyomi.ui.base.activity
|
package eu.kanade.tachiyomi.ui.base.activity
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import nucleus.view.NucleusAppCompatActivity
|
import nucleus.view.NucleusAppCompatActivity
|
||||||
|
|
||||||
@ -11,4 +13,14 @@ abstract class BaseRxActivity<P : BasePresenter<*>> : NucleusAppCompatActivity<P
|
|||||||
LocaleHelper.updateConfiguration(this)
|
LocaleHelper.updateConfiguration(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
SecureActivityDelegate.setSecure(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
SecureActivityDelegate.promptLockIfNeeded(this)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ import android.webkit.WebView
|
|||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
|
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
|
||||||
import androidx.biometric.BiometricManager
|
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.bluelinelabs.conductor.Conductor
|
import com.bluelinelabs.conductor.Conductor
|
||||||
@ -53,6 +52,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
|
|||||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController
|
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController
|
||||||
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
|
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
|
||||||
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
|
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
|
||||||
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
import eu.kanade.tachiyomi.util.system.launchUI
|
||||||
@ -353,22 +353,10 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
// setting in case someone comes from the search activity
|
// setting in case someone comes from the search activity to main
|
||||||
usingBottomNav = true
|
usingBottomNav = true
|
||||||
getExtensionUpdates()
|
getExtensionUpdates()
|
||||||
DownloadService.callListeners()
|
DownloadService.callListeners()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExtensionUpdates() {
|
private fun getExtensionUpdates() {
|
||||||
@ -480,7 +468,7 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
|||||||
val baseController = router.backstack.last().controller() as? BaseController
|
val baseController = router.backstack.last().controller() as? BaseController
|
||||||
if (if (router.backstackSize == 1) !(baseController?.handleRootBack() ?: false)
|
if (if (router.backstackSize == 1) !(baseController?.handleRootBack() ?: false)
|
||||||
else !router.handleBack()) {
|
else !router.handleBack()) {
|
||||||
unlocked = false
|
SecureActivityDelegate.locked = true
|
||||||
super.onBackPressed()
|
super.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -593,8 +581,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
|||||||
|
|
||||||
private const val URL_HELP = "https://tachiyomi.org/help/"
|
private const val URL_HELP = "https://tachiyomi.org/help/"
|
||||||
|
|
||||||
var unlocked = false
|
|
||||||
|
|
||||||
var usingBottomNav = true
|
var usingBottomNav = true
|
||||||
internal set
|
internal set
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
|
|||||||
import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
|
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
|
||||||
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||||
import eu.kanade.tachiyomi.util.view.updatePadding
|
import eu.kanade.tachiyomi.util.view.updatePadding
|
||||||
@ -148,7 +149,7 @@ class SearchActivity: MainActivity() {
|
|||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if (router.backstack.size <= 1 || !router.handleBack()) {
|
if (router.backstack.size <= 1 || !router.handleBack()) {
|
||||||
unlocked = false
|
SecureActivityDelegate.locked = true
|
||||||
super.onBackPressed()
|
super.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.ui.manga
|
package eu.kanade.tachiyomi.ui.manga
|
||||||
|
|
||||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
import android.app.Activity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -29,10 +30,11 @@ import eu.kanade.tachiyomi.ui.main.SearchActivity
|
|||||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersController
|
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersController
|
||||||
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController
|
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController
|
||||||
import eu.kanade.tachiyomi.ui.manga.track.TrackController
|
import eu.kanade.tachiyomi.ui.manga.track.TrackController
|
||||||
import kotlinx.android.synthetic.main.search_activity.sTabs
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.android.synthetic.main.main_activity.tabs
|
import kotlinx.android.synthetic.main.main_activity.*
|
||||||
import kotlinx.android.synthetic.main.manga_controller.manga_pager
|
import kotlinx.android.synthetic.main.manga_controller.*
|
||||||
|
import kotlinx.android.synthetic.main.search_activity.*
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
@ -98,6 +100,8 @@ class MangaController : RxController, TabbedController {
|
|||||||
|
|
||||||
var startingChapterYPos:Float? = null
|
var startingChapterYPos:Float? = null
|
||||||
|
|
||||||
|
var isLockedFromSearch = false
|
||||||
|
|
||||||
private var adapter: MangaDetailAdapter? = null
|
private var adapter: MangaDetailAdapter? = null
|
||||||
|
|
||||||
val fromCatalogue = args.getBoolean(FROM_CATALOGUE_EXTRA, false)
|
val fromCatalogue = args.getBoolean(FROM_CATALOGUE_EXTRA, false)
|
||||||
@ -131,6 +135,9 @@ class MangaController : RxController, TabbedController {
|
|||||||
manga_pager.offscreenPageLimit = 3
|
manga_pager.offscreenPageLimit = 3
|
||||||
manga_pager.adapter = adapter
|
manga_pager.adapter = adapter
|
||||||
|
|
||||||
|
isLockedFromSearch = activity is SearchActivity &&
|
||||||
|
SecureActivityDelegate.shouldBeLocked()
|
||||||
|
|
||||||
if (!fromCatalogue)
|
if (!fromCatalogue)
|
||||||
manga_pager.currentItem = CHAPTERS_CONTROLLER
|
manga_pager.currentItem = CHAPTERS_CONTROLLER
|
||||||
}
|
}
|
||||||
@ -140,6 +147,12 @@ class MangaController : RxController, TabbedController {
|
|||||||
adapter = null
|
adapter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onActivityResumed(activity: Activity) {
|
||||||
|
super.onActivityResumed(activity)
|
||||||
|
isLockedFromSearch = activity is SearchActivity &&
|
||||||
|
SecureActivityDelegate.shouldBeLocked()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||||
super.onChangeStarted(handler, type)
|
super.onChangeStarted(handler, type)
|
||||||
if (type.isEnter) {
|
if (type.isEnter) {
|
||||||
|
@ -8,9 +8,11 @@ import eu.kanade.tachiyomi.data.download.model.Download
|
|||||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
import eu.kanade.tachiyomi.util.view.gone
|
import eu.kanade.tachiyomi.util.view.gone
|
||||||
|
import eu.kanade.tachiyomi.util.view.invisible
|
||||||
import eu.kanade.tachiyomi.util.view.setVectorCompat
|
import eu.kanade.tachiyomi.util.view.setVectorCompat
|
||||||
|
import eu.kanade.tachiyomi.util.view.visible
|
||||||
import kotlinx.android.synthetic.main.chapters_item.*
|
import kotlinx.android.synthetic.main.chapters_item.*
|
||||||
import java.util.*
|
import java.util.Date
|
||||||
|
|
||||||
class ChapterHolder(
|
class ChapterHolder(
|
||||||
private val view: View,
|
private val view: View,
|
||||||
@ -26,7 +28,7 @@ class ChapterHolder(
|
|||||||
|
|
||||||
fun bind(item: ChapterItem, manga: Manga) {
|
fun bind(item: ChapterItem, manga: Manga) {
|
||||||
val chapter = item.chapter
|
val chapter = item.chapter
|
||||||
|
val isLocked = item.isLocked
|
||||||
chapter_title.text = when (manga.displayMode) {
|
chapter_title.text = when (manga.displayMode) {
|
||||||
Manga.DISPLAY_NUMBER -> {
|
Manga.DISPLAY_NUMBER -> {
|
||||||
val number = adapter.decimalFormat.format(chapter.chapter_number.toDouble())
|
val number = adapter.decimalFormat.format(chapter.chapter_number.toDouble())
|
||||||
@ -35,12 +37,16 @@ class ChapterHolder(
|
|||||||
else -> chapter.name
|
else -> chapter.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chapter_menu.visible()
|
||||||
// Set the correct drawable for dropdown and update the tint to match theme.
|
// Set the correct drawable for dropdown and update the tint to match theme.
|
||||||
chapter_menu.setVectorCompat(R.drawable.ic_more_vert_black_24dp, view.context.getResourceColor(R.attr.icon_color))
|
chapter_menu.setVectorCompat(R.drawable.ic_more_vert_black_24dp, view.context.getResourceColor(R.attr.icon_color))
|
||||||
|
|
||||||
|
if (isLocked) chapter_menu.invisible()
|
||||||
|
|
||||||
// Set correct text color
|
// Set correct text color
|
||||||
chapter_title.setTextColor(if (chapter.read) adapter.readColor else adapter.unreadColor)
|
chapter_title.setTextColor(if (chapter.read && !isLocked)
|
||||||
if (chapter.bookmark) chapter_title.setTextColor(adapter.bookmarkedColor)
|
adapter.readColor else adapter.unreadColor)
|
||||||
|
if (chapter.bookmark && !isLocked) chapter_title.setTextColor(adapter.bookmarkedColor)
|
||||||
|
|
||||||
if (chapter.date_upload > 0) {
|
if (chapter.date_upload > 0) {
|
||||||
chapter_date.text = adapter.dateFormat.format(Date(chapter.date_upload))
|
chapter_date.text = adapter.dateFormat.format(Date(chapter.date_upload))
|
||||||
@ -59,7 +65,7 @@ class ChapterHolder(
|
|||||||
chapter_title.maxLines = 1
|
chapter_title.maxLines = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
chapter_pages.text = if (!chapter.read && chapter.last_page_read > 0) {
|
chapter_pages.text = if (!chapter.read && chapter.last_page_read > 0 && !isLocked) {
|
||||||
itemView.context.getString(R.string.chapter_progress, chapter.last_page_read + 1)
|
itemView.context.getString(R.string.chapter_progress, chapter.last_page_read + 1)
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
@ -81,6 +87,10 @@ class ChapterHolder(
|
|||||||
private fun showPopupMenu(view: View) {
|
private fun showPopupMenu(view: View) {
|
||||||
val item = adapter.getItem(adapterPosition) ?: return
|
val item = adapter.getItem(adapterPosition) ?: return
|
||||||
|
|
||||||
|
if (item.isLocked) {
|
||||||
|
adapter.unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
// Create a PopupMenu, giving it the clicked view for an anchor
|
// Create a PopupMenu, giving it the clicked view for an anchor
|
||||||
val popup = PopupMenu(view.context, view)
|
val popup = PopupMenu(view.context, view)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ class ChapterItem(val chapter: Chapter, val manga: Manga) : AbstractFlexibleItem
|
|||||||
Chapter by chapter {
|
Chapter by chapter {
|
||||||
|
|
||||||
private var _status: Int = 0
|
private var _status: Int = 0
|
||||||
|
var isLocked = false
|
||||||
|
|
||||||
var status: Int
|
var status: Int
|
||||||
get() = download?.status ?: _status
|
get() = download?.status ?: _status
|
||||||
|
@ -2,18 +2,20 @@ package eu.kanade.tachiyomi.ui.manga.chapter
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import java.text.DecimalFormatSymbols
|
import java.text.DecimalFormatSymbols
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
|
|
||||||
class ChaptersAdapter(
|
class ChaptersAdapter(
|
||||||
controller: ChaptersController,
|
val controller: ChaptersController,
|
||||||
context: Context
|
context: Context
|
||||||
) : FlexibleAdapter<ChapterItem>(null, controller, true) {
|
) : FlexibleAdapter<ChapterItem>(null, controller, true) {
|
||||||
|
|
||||||
@ -43,8 +45,12 @@ class ChaptersAdapter(
|
|||||||
return items.indexOf(item)
|
return items.indexOf(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun unlock() {
|
||||||
|
val activity = controller.activity as? FragmentActivity ?: return
|
||||||
|
SecureActivityDelegate.promptLockIfNeeded(activity)
|
||||||
|
}
|
||||||
|
|
||||||
interface OnMenuItemClickListener {
|
interface OnMenuItemClickListener {
|
||||||
fun onMenuItemClick(position: Int, item: MenuItem)
|
fun onMenuItemClick(position: Int, item: MenuItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,12 @@ import eu.kanade.tachiyomi.R
|
|||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
|
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
|
||||||
import eu.kanade.tachiyomi.util.view.getCoordinates
|
import eu.kanade.tachiyomi.util.view.getCoordinates
|
||||||
@ -95,6 +95,7 @@ class ChaptersController() : NucleusController<ChaptersPresenter>(),
|
|||||||
|
|
||||||
// Init RecyclerView and adapter
|
// Init RecyclerView and adapter
|
||||||
adapter = ChaptersAdapter(this, view.context)
|
adapter = ChaptersAdapter(this, view.context)
|
||||||
|
setReadingDrawable()
|
||||||
|
|
||||||
recycler.adapter = adapter
|
recycler.adapter = adapter
|
||||||
recycler.layoutManager = LinearLayoutManager(view.context)
|
recycler.layoutManager = LinearLayoutManager(view.context)
|
||||||
@ -117,6 +118,10 @@ class ChaptersController() : NucleusController<ChaptersPresenter>(),
|
|||||||
swipe_refresh.refreshes().subscribeUntilDestroy { fetchChaptersFromSource() }
|
swipe_refresh.refreshes().subscribeUntilDestroy { fetchChaptersFromSource() }
|
||||||
|
|
||||||
fab.clicks().subscribeUntilDestroy {
|
fab.clicks().subscribeUntilDestroy {
|
||||||
|
if (activity is SearchActivity && presenter.isLockedFromSearch) {
|
||||||
|
SecureActivityDelegate.promptLockIfNeeded(activity)
|
||||||
|
return@subscribeUntilDestroy
|
||||||
|
}
|
||||||
val item = presenter.getNextUnreadChapter()
|
val item = presenter.getNextUnreadChapter()
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
// Create animation listener
|
// Create animation listener
|
||||||
@ -149,9 +154,29 @@ class ChaptersController() : NucleusController<ChaptersPresenter>(),
|
|||||||
actionMode = null
|
actionMode = null
|
||||||
super.onDestroyView(view)
|
super.onDestroyView(view)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Update FAB with correct drawable.
|
||||||
|
*
|
||||||
|
* @param isFavorite determines if manga is favorite or not.
|
||||||
|
*/
|
||||||
|
private fun setReadingDrawable() {
|
||||||
|
// Set the Favorite drawable to the correct one.
|
||||||
|
// Border drawable if false, filled drawable if true.
|
||||||
|
fab.setImageResource(
|
||||||
|
when {
|
||||||
|
(parentController as MangaController).isLockedFromSearch -> R.drawable.ic_lock_white_24dp
|
||||||
|
else -> R.drawable.ic_play_arrow_white_24dp
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onActivityResumed(activity: Activity) {
|
override fun onActivityResumed(activity: Activity) {
|
||||||
|
super.onActivityResumed(activity)
|
||||||
if (view == null) return
|
if (view == null) return
|
||||||
|
if (activity is SearchActivity) {
|
||||||
|
presenter.updateLockStatus()
|
||||||
|
setReadingDrawable()
|
||||||
|
}
|
||||||
|
|
||||||
// Check if animation view is visible
|
// Check if animation view is visible
|
||||||
if (reveal_view.visibility == View.VISIBLE) {
|
if (reveal_view.visibility == View.VISIBLE) {
|
||||||
@ -159,10 +184,10 @@ class ChaptersController() : NucleusController<ChaptersPresenter>(),
|
|||||||
val coordinates = fab.getCoordinates()
|
val coordinates = fab.getCoordinates()
|
||||||
reveal_view.hideRevealEffect(coordinates.x, coordinates.y, 1920)
|
reveal_view.hideRevealEffect(coordinates.x, coordinates.y, 1920)
|
||||||
}
|
}
|
||||||
super.onActivityResumed(activity)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
if (!(parentController as MangaController).isLockedFromSearch)
|
||||||
inflater.inflate(R.menu.chapters, menu)
|
inflater.inflate(R.menu.chapters, menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed
|
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -65,14 +66,28 @@ class ChaptersPresenter(
|
|||||||
*/
|
*/
|
||||||
private var observeDownloadsSubscription: Subscription? = null
|
private var observeDownloadsSubscription: Subscription? = null
|
||||||
|
|
||||||
|
var isLockedFromSearch = false
|
||||||
|
|
||||||
|
fun updateLockStatus() {
|
||||||
|
val lastCheck = isLockedFromSearch
|
||||||
|
isLockedFromSearch = SecureActivityDelegate.shouldBeLocked()
|
||||||
|
if (lastCheck && lastCheck != isLockedFromSearch) {
|
||||||
|
chapters.forEach {
|
||||||
|
it.isLocked = false
|
||||||
|
}
|
||||||
|
chaptersRelay.call(chapters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
override fun onCreate(savedState: Bundle?) {
|
||||||
super.onCreate(savedState)
|
super.onCreate(savedState)
|
||||||
|
isLockedFromSearch = SecureActivityDelegate.shouldBeLocked()
|
||||||
|
|
||||||
// Prepare the relay.
|
// Prepare the relay.
|
||||||
chaptersRelay.flatMap { applyChapterFilters(it) }
|
chaptersRelay.flatMap { applyChapterFilters(it) }
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribeLatestCache(ChaptersController::onNextChapters,
|
.subscribeLatestCache(ChaptersController::onNextChapters
|
||||||
{ _, error -> Timber.e(error) })
|
) { _, error -> Timber.e(error) }
|
||||||
|
|
||||||
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
|
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
|
||||||
// changes, and sends the list of chapters to the relay.
|
// changes, and sends the list of chapters to the relay.
|
||||||
@ -120,6 +135,7 @@ class ChaptersPresenter(
|
|||||||
private fun Chapter.toModel(): ChapterItem {
|
private fun Chapter.toModel(): ChapterItem {
|
||||||
// Create the model object.
|
// Create the model object.
|
||||||
val model = ChapterItem(this, manga)
|
val model = ChapterItem(this, manga)
|
||||||
|
model.isLocked = isLockedFromSearch
|
||||||
|
|
||||||
// Find an active download for this chapter.
|
// Find an active download for this chapter.
|
||||||
val download = downloadManager.queue.find { it.chapter.id == id }
|
val download = downloadManager.queue.find { it.chapter.id == id }
|
||||||
|
@ -4,6 +4,7 @@ import android.animation.Animator
|
|||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
import android.animation.AnimatorSet
|
import android.animation.AnimatorSet
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
@ -61,26 +62,19 @@ import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
|
|||||||
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
|
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
import eu.kanade.tachiyomi.ui.library.LibraryController
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
|
|
||||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
|
||||||
import eu.kanade.tachiyomi.util.view.marginBottom
|
import eu.kanade.tachiyomi.util.view.marginBottom
|
||||||
import eu.kanade.tachiyomi.util.view.snack
|
import eu.kanade.tachiyomi.util.view.snack
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||||
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
|
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
|
||||||
import jp.wasabeef.glide.transformations.CropSquareTransformation
|
import jp.wasabeef.glide.transformations.CropSquareTransformation
|
||||||
import jp.wasabeef.glide.transformations.MaskTransformation
|
import jp.wasabeef.glide.transformations.MaskTransformation
|
||||||
import kotlinx.android.synthetic.main.edit_manga_dialog.*
|
|
||||||
import kotlinx.android.synthetic.main.manga_info_controller.*
|
import kotlinx.android.synthetic.main.manga_info_controller.*
|
||||||
import kotlinx.android.synthetic.main.manga_info_controller.manga_artist
|
|
||||||
import kotlinx.android.synthetic.main.manga_info_controller.manga_author
|
|
||||||
import kotlinx.android.synthetic.main.manga_info_controller.manga_cover
|
|
||||||
import kotlinx.android.synthetic.main.manga_info_controller.manga_genres_tags
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
@ -238,7 +232,8 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
|||||||
inflater.inflate(R.menu.manga_info, menu)
|
inflater.inflate(R.menu.manga_info, menu)
|
||||||
|
|
||||||
val editItem = menu.findItem(R.id.action_edit)
|
val editItem = menu.findItem(R.id.action_edit)
|
||||||
editItem.isVisible = presenter.manga.favorite
|
editItem.isVisible = presenter.manga.favorite &&
|
||||||
|
!(parentController as MangaController).isLockedFromSearch
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
@ -365,6 +360,11 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onActivityResumed(activity: Activity) {
|
||||||
|
super.onActivityResumed(activity)
|
||||||
|
setFavoriteDrawable(presenter.manga.favorite)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView(view: View) {
|
override fun onDestroyView(view: View) {
|
||||||
manga_genres_tags.setOnTagClickListener(null)
|
manga_genres_tags.setOnTagClickListener(null)
|
||||||
snack?.dismiss()
|
snack?.dismiss()
|
||||||
@ -466,10 +466,13 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
|||||||
private fun setFavoriteDrawable(isFavorite: Boolean) {
|
private fun setFavoriteDrawable(isFavorite: Boolean) {
|
||||||
// Set the Favorite drawable to the correct one.
|
// Set the Favorite drawable to the correct one.
|
||||||
// Border drawable if false, filled drawable if true.
|
// Border drawable if false, filled drawable if true.
|
||||||
fab_favorite?.setImageResource(if (isFavorite)
|
fab_favorite?.setImageResource(
|
||||||
R.drawable.ic_bookmark_white_24dp
|
when {
|
||||||
else
|
(parentController as MangaController).isLockedFromSearch -> R.drawable.ic_lock_white_24dp
|
||||||
R.drawable.ic_add_to_library_24dp)
|
isFavorite -> R.drawable.ic_bookmark_white_24dp
|
||||||
|
else -> R.drawable.ic_add_to_library_24dp
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -510,6 +513,10 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
|||||||
* Called when the fab is clicked.
|
* Called when the fab is clicked.
|
||||||
*/
|
*/
|
||||||
private fun onFabClick() {
|
private fun onFabClick() {
|
||||||
|
if ((parentController as MangaController).isLockedFromSearch) {
|
||||||
|
SecureActivityDelegate.promptLockIfNeeded(activity)
|
||||||
|
return
|
||||||
|
}
|
||||||
val manga = presenter.manga
|
val manga = presenter.manga
|
||||||
toggleFavorite()
|
toggleFavorite()
|
||||||
if (manga.favorite) {
|
if (manga.favorite) {
|
||||||
@ -689,6 +696,8 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
|||||||
* @param query the search query to pass to the search controller
|
* @param query the search query to pass to the search controller
|
||||||
*/
|
*/
|
||||||
private fun performGlobalSearch(query: String) {
|
private fun performGlobalSearch(query: String) {
|
||||||
|
if ((parentController as MangaController).isLockedFromSearch)
|
||||||
|
return
|
||||||
val router = parentController?.router ?: return
|
val router = parentController?.router ?: return
|
||||||
router.pushController(CatalogueSearchController(query).withFadeTransaction())
|
router.pushController(CatalogueSearchController(query).withFadeTransaction())
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.ui.manga.track
|
package eu.kanade.tachiyomi.ui.manga.track
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -11,8 +12,12 @@ import eu.kanade.tachiyomi.R
|
|||||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
|
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
|
||||||
|
import eu.kanade.tachiyomi.util.view.gone
|
||||||
|
import eu.kanade.tachiyomi.util.view.invisible
|
||||||
|
import eu.kanade.tachiyomi.util.view.visible
|
||||||
import kotlinx.android.synthetic.main.track_controller.*
|
import kotlinx.android.synthetic.main.track_controller.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ -41,14 +46,32 @@ class TrackController : NucleusController<TrackPresenter>(),
|
|||||||
override fun onViewCreated(view: View) {
|
override fun onViewCreated(view: View) {
|
||||||
super.onViewCreated(view)
|
super.onViewCreated(view)
|
||||||
|
|
||||||
|
if ((parentController as MangaController).isLockedFromSearch) {
|
||||||
|
swipe_refresh.invisible()
|
||||||
|
unlock_button.visible()
|
||||||
|
unlock_button.setOnClickListener {
|
||||||
|
SecureActivityDelegate.promptLockIfNeeded(activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
adapter = TrackAdapter(this)
|
adapter = TrackAdapter(this)
|
||||||
with(view) {
|
track_recycler.layoutManager = LinearLayoutManager(view.context)
|
||||||
track_recycler.layoutManager = LinearLayoutManager(context)
|
|
||||||
track_recycler.adapter = adapter
|
track_recycler.adapter = adapter
|
||||||
track_recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
|
track_recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
|
||||||
swipe_refresh.isEnabled = false
|
swipe_refresh.isEnabled = false
|
||||||
swipe_refresh.refreshes().subscribeUntilDestroy { presenter.refresh() }
|
swipe_refresh.refreshes().subscribeUntilDestroy { presenter.refresh() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showTracking() {
|
||||||
|
swipe_refresh.visible()
|
||||||
|
unlock_button.gone()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResumed(activity: Activity) {
|
||||||
|
super.onActivityResumed(activity)
|
||||||
|
if (!(parentController as MangaController).isLockedFromSearch) {
|
||||||
|
showTracking()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView(view: View) {
|
override fun onDestroyView(view: View) {
|
||||||
|
@ -21,7 +21,6 @@ import android.view.animation.Animation
|
|||||||
import android.view.animation.AnimationUtils
|
import android.view.animation.AnimationUtils
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.biometric.BiometricManager
|
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
@ -32,8 +31,6 @@ import eu.kanade.tachiyomi.data.notification.Notifications
|
|||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
|
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.AddToLibraryFirst
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Error
|
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Error
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Success
|
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Success
|
||||||
@ -45,11 +42,12 @@ 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.R2LPagerViewer
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
|
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.lang.plusAssign
|
import eu.kanade.tachiyomi.util.lang.plusAssign
|
||||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||||
import eu.kanade.tachiyomi.util.system.GLUtil
|
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
|
import eu.kanade.tachiyomi.util.system.launchUI
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.view.gone
|
import eu.kanade.tachiyomi.util.view.gone
|
||||||
import eu.kanade.tachiyomi.util.view.visible
|
import eu.kanade.tachiyomi.util.view.visible
|
||||||
@ -67,7 +65,6 @@ import rx.subscriptions.CompositeSubscription
|
|||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Date
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
@ -527,23 +524,6 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
|
|||||||
presenter.shareImage(page)
|
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
|
* Called from the presenter when a page is ready to be shared. It shows Android's default
|
||||||
* sharing tool.
|
* sharing tool.
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
package eu.kanade.tachiyomi.ui.main
|
package eu.kanade.tachiyomi.ui.security
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.biometric.BiometricPrompt
|
import androidx.biometric.BiometricPrompt
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
class BiometricActivity : BaseActivity() {
|
class BiometricActivity : BaseActivity() {
|
||||||
val executor = Executors.newSingleThreadExecutor()
|
private val executor: ExecutorService = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -24,15 +23,10 @@ class BiometricActivity : BaseActivity() {
|
|||||||
|
|
||||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||||
super.onAuthenticationSucceeded(result)
|
super.onAuthenticationSucceeded(result)
|
||||||
MainActivity.unlocked = true
|
SecureActivityDelegate.locked = false
|
||||||
preferences.lastUnlock().set(Date().time)
|
preferences.lastUnlock().set(Date().time)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAuthenticationFailed() {
|
|
||||||
super.onAuthenticationFailed()
|
|
||||||
// TODO("Called when a biometric is valid but not recognized.")
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
val promptInfo = BiometricPrompt.PromptInfo.Builder()
|
val promptInfo = BiometricPrompt.PromptInfo.Builder()
|
@ -0,0 +1,57 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.security
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.biometric.BiometricManager
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
object SecureActivityDelegate {
|
||||||
|
|
||||||
|
private val preferences by injectLazy<PreferencesHelper>()
|
||||||
|
|
||||||
|
var locked: Boolean = true
|
||||||
|
|
||||||
|
fun setSecure(activity: Activity?, force:Boolean? = null) {
|
||||||
|
val enabled = force ?: preferences.secureScreen().getOrDefault()
|
||||||
|
if (enabled) {
|
||||||
|
activity?.window?.setFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE,
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun promptLockIfNeeded(activity: Activity?) {
|
||||||
|
if (activity == null) return
|
||||||
|
val lockApp = preferences.useBiometrics().getOrDefault()
|
||||||
|
if (lockApp && BiometricManager.from(activity).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
|
||||||
|
if (isAppLocked()) {
|
||||||
|
val intent = Intent(activity, BiometricActivity::class.java)
|
||||||
|
activity.startActivity(intent)
|
||||||
|
activity.overridePendingTransition(0, 0)
|
||||||
|
}
|
||||||
|
} else if (lockApp) {
|
||||||
|
preferences.useBiometrics().set(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldBeLocked(): Boolean {
|
||||||
|
val lockApp = preferences.useBiometrics().getOrDefault()
|
||||||
|
if (lockApp && isAppLocked()) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isAppLocked(): Boolean {
|
||||||
|
return locked &&
|
||||||
|
(preferences.lockAfter().getOrDefault() <= 0
|
||||||
|
|| Date().time >= preferences.lastUnlock().getOrDefault() + 60 * 1000 * preferences
|
||||||
|
.lockAfter().getOrDefault())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,17 +1,14 @@
|
|||||||
package eu.kanade.tachiyomi.ui.setting
|
package eu.kanade.tachiyomi.ui.setting
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.biometric.BiometricManager
|
import androidx.biometric.BiometricManager
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.data.updater.UpdaterJob
|
import eu.kanade.tachiyomi.data.updater.UpdaterJob
|
||||||
import eu.kanade.tachiyomi.widget.preference.IntListMatPreference
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.widget.preference.IntListMatPreference
|
||||||
import kotlinx.android.synthetic.main.main_activity.*
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
||||||
|
|
||||||
class SettingsGeneralController : SettingsController() {
|
class SettingsGeneralController : SettingsController() {
|
||||||
@ -97,9 +94,12 @@ class SettingsGeneralController : SettingsController() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preferenceCategory {
|
||||||
|
titleRes = R.string.pref_category_security
|
||||||
|
|
||||||
val biometricManager = BiometricManager.from(context)
|
val biometricManager = BiometricManager.from(context)
|
||||||
if (biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
|
if (biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
|
||||||
var preference:IntListMatPreference? = null
|
var preference: IntListMatPreference? = null
|
||||||
switchPreference {
|
switchPreference {
|
||||||
key = Keys.useBiometrics
|
key = Keys.useBiometrics
|
||||||
titleRes = R.string.lock_with_biometrics
|
titleRes = R.string.lock_with_biometrics
|
||||||
@ -119,13 +119,28 @@ class SettingsGeneralController : SettingsController() {
|
|||||||
when (it) {
|
when (it) {
|
||||||
0 -> context.getString(R.string.lock_always)
|
0 -> context.getString(R.string.lock_always)
|
||||||
-1 -> context.getString(R.string.lock_never)
|
-1 -> context.getString(R.string.lock_never)
|
||||||
else -> resources?.getQuantityString(R.plurals.lock_after_mins, it.toInt(),
|
else -> resources?.getQuantityString(
|
||||||
it)
|
R.plurals.lock_after_mins, it.toInt(), it
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entryValues = values
|
entryValues = values
|
||||||
defaultValue = 0
|
defaultValue = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switchPreference {
|
||||||
|
key = Keys.secureScreen
|
||||||
|
titleRes = R.string.pref_secure_screen
|
||||||
|
summaryRes = R.string.pref_secure_screen_summary
|
||||||
|
defaultValue = false
|
||||||
|
|
||||||
|
onChange {
|
||||||
|
it as Boolean
|
||||||
|
SecureActivityDelegate.setSecure(activity, it)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,8 @@ class SettingsLibraryController : SettingsController() {
|
|||||||
intListPreference(activity) {
|
intListPreference(activity) {
|
||||||
titleRes = R.string.pref_keep_category_sorting
|
titleRes = R.string.pref_keep_category_sorting
|
||||||
key = Keys.keepCatSort
|
key = Keys.keepCatSort
|
||||||
summaryRes = R.string.pref_keep_category_sorting_summary
|
|
||||||
|
customSummary = context.getString(R.string.pref_keep_category_sorting_summary)
|
||||||
entries = listOf(
|
entries = listOf(
|
||||||
context.getString(R.string.always_ask),
|
context.getString(R.string.always_ask),
|
||||||
context.getString(R.string.option_keep_category_sort),
|
context.getString(R.string.option_keep_category_sort),
|
||||||
|
@ -27,7 +27,8 @@ AttributeSet? =
|
|||||||
defValue = defaultValue as? Int ?: defValue
|
defValue = defaultValue as? Int ?: defValue
|
||||||
}
|
}
|
||||||
override fun getSummary(): CharSequence {
|
override fun getSummary(): CharSequence {
|
||||||
if (key == null || useCustomSummary) return super.getSummary()
|
if (customSummary != null) return customSummary!!
|
||||||
|
if (key == null) return super.getSummary()
|
||||||
val index = entryValues.indexOf(prefs.getInt(key, defValue).getOrDefault())
|
val index = entryValues.indexOf(prefs.getInt(key, defValue).getOrDefault())
|
||||||
return if (entries.isEmpty() || index == -1) ""
|
return if (entries.isEmpty() || index == -1) ""
|
||||||
else entries[index]
|
else entries[index]
|
||||||
|
@ -3,17 +3,11 @@ package eu.kanade.tachiyomi.widget.preference
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.ui.setting.defaultValue
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
|
|
||||||
open class ListMatPreference @JvmOverloads constructor(activity: Activity?, context: Context,
|
open class ListMatPreference @JvmOverloads constructor(activity: Activity?, context: Context,
|
||||||
attrs: AttributeSet? =
|
attrs: AttributeSet? =
|
||||||
@ -34,6 +28,7 @@ open class ListMatPreference @JvmOverloads constructor(activity: Activity?, cont
|
|||||||
defValue = defaultValue as? String ?: defValue
|
defValue = defaultValue as? String ?: defValue
|
||||||
}
|
}
|
||||||
override fun getSummary(): CharSequence {
|
override fun getSummary(): CharSequence {
|
||||||
|
if (customSummary != null) return customSummary!!
|
||||||
val index = entryValues.indexOf(prefs.getStringPref(key, defValue).getOrDefault())
|
val index = entryValues.indexOf(prefs.getStringPref(key, defValue).getOrDefault())
|
||||||
return if (entries.isEmpty() || index == -1) ""
|
return if (entries.isEmpty() || index == -1) ""
|
||||||
else entries[index]
|
else entries[index]
|
||||||
|
@ -16,19 +16,10 @@ open class MatPreference @JvmOverloads constructor(val activity: Activity?, cont
|
|||||||
null) :
|
null) :
|
||||||
Preference(context, attrs) {
|
Preference(context, attrs) {
|
||||||
|
|
||||||
protected var useCustomSummary = false
|
|
||||||
protected val prefs: PreferencesHelper = Injekt.get()
|
protected val prefs: PreferencesHelper = Injekt.get()
|
||||||
private var isShowing = false
|
private var isShowing = false
|
||||||
|
var customSummary:String? = null
|
||||||
|
|
||||||
override fun setSummary(summaryResId: Int) {
|
|
||||||
useCustomSummary = true
|
|
||||||
super.setSummary(summaryResId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setSummary(summary: CharSequence?) {
|
|
||||||
useCustomSummary = true
|
|
||||||
super.setSummary(summary)
|
|
||||||
}
|
|
||||||
override fun onClick() {
|
override fun onClick() {
|
||||||
if (!isShowing)
|
if (!isShowing)
|
||||||
dialog().apply {
|
dialog().apply {
|
||||||
@ -37,6 +28,10 @@ open class MatPreference @JvmOverloads constructor(val activity: Activity?, cont
|
|||||||
isShowing = true
|
isShowing = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getSummary(): CharSequence {
|
||||||
|
return customSummary ?: super.getSummary()
|
||||||
|
}
|
||||||
|
|
||||||
open fun dialog(): MaterialDialog {
|
open fun dialog(): MaterialDialog {
|
||||||
return MaterialDialog(activity ?: context).apply {
|
return MaterialDialog(activity ?: context).apply {
|
||||||
if (title != null)
|
if (title != null)
|
||||||
|
@ -20,10 +20,15 @@ class MultiListMatPreference @JvmOverloads constructor(activity: Activity?, cont
|
|||||||
var customSummaryRes:Int
|
var customSummaryRes:Int
|
||||||
get() = 0
|
get() = 0
|
||||||
set(value) { customSummary = context.getString(value) }
|
set(value) { customSummary = context.getString(value) }
|
||||||
var customSummary:String? = null
|
|
||||||
|
|
||||||
override fun getSummary(): CharSequence {
|
override fun getSummary(): CharSequence {
|
||||||
return customSummary ?: super.getSummary()
|
if (customSummary != null) return customSummary!!
|
||||||
|
return prefs.getStringSet(key, emptySet<String>()).getOrDefault().mapNotNull {
|
||||||
|
if (entryValues.indexOf(it) == -1) null
|
||||||
|
else entryValues.indexOf(it) + if (allSelectionRes != null) 1 else 0
|
||||||
|
}.toIntArray().joinToString(",") {
|
||||||
|
entries[it]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
|
5
app/src/main/res/drawable/ic_lock_white_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_lock_white_24dp.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||||
|
</vector>
|
@ -1,8 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/swipe_refresh"
|
android:id="@+id/swipe_refresh"
|
||||||
@ -20,4 +22,17 @@
|
|||||||
|
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/unlock_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:layout_gravity="center|top"
|
||||||
|
app:rippleColor="@color/fullRippleColor"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:textColor="?android:attr/colorAccent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/action_unlock_trackers"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
<string name="hiding_categories">Hiding categories</string>
|
<string name="hiding_categories">Hiding categories</string>
|
||||||
<string name="manga_only">Manga only</string>
|
<string name="manga_only">Manga only</string>
|
||||||
<string name="manwha_only">Manwha only</string>
|
<string name="manwha_only">Manwha only</string>
|
||||||
|
<string name="action_unlock_trackers">Unlock to access trackers</string>
|
||||||
|
|
||||||
<string name="sorting_by_">Sorting by %1$s</string>
|
<string name="sorting_by_">Sorting by %1$s</string>
|
||||||
<string name="sort_by_">Sort by: %1$s</string>
|
<string name="sort_by_">Sort by: %1$s</string>
|
||||||
@ -169,6 +170,9 @@
|
|||||||
<string name="pref_date_format">Date format</string>
|
<string name="pref_date_format">Date format</string>
|
||||||
<string name="pref_enable_automatic_updates">Check for updates</string>
|
<string name="pref_enable_automatic_updates">Check for updates</string>
|
||||||
<string name="pref_enable_automatic_updates_summary">Automatically check for new app versions</string>
|
<string name="pref_enable_automatic_updates_summary">Automatically check for new app versions</string>
|
||||||
|
<string name="pref_secure_screen">Secure screen</string>
|
||||||
|
<string name="pref_secure_screen_summary">Hide Tachiyomi from the overview screen</string>
|
||||||
|
<string name="pref_category_security">Security</string>
|
||||||
|
|
||||||
<!-- Library section -->
|
<!-- Library section -->
|
||||||
<string name="pref_category_library_display">Display</string>
|
<string name="pref_category_library_display">Display</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user