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:configChanges="uiMode|orientation|screenSize"/>
|
||||
<activity
|
||||
android:name=".ui.main.BiometricActivity" />
|
||||
android:name=".ui.security.BiometricActivity" />
|
||||
<activity
|
||||
android:name=".widget.CustomLayoutPickerActivity"
|
||||
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.updater.UpdaterJob
|
||||
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 org.acra.ACRA
|
||||
import org.acra.annotation.ReportsCrashes
|
||||
@ -55,7 +55,7 @@ open class App : Application(), LifecycleObserver {
|
||||
//App in background
|
||||
val preferences: PreferencesHelper by injectLazy()
|
||||
if (preferences.lockAfter().getOrDefault() >= 0) {
|
||||
MainActivity.unlocked = false
|
||||
SecureActivityDelegate.locked = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +131,8 @@ object PreferenceKeys {
|
||||
|
||||
const val lastUnlock = "last_unlock"
|
||||
|
||||
const val secureScreen = "secure_screen"
|
||||
|
||||
const val removeArticles = "remove_articles"
|
||||
|
||||
const val skipPreMigration = "skip_pre_migration"
|
||||
|
@ -215,6 +215,8 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun lastUnlock() = rxPrefs.getLong(Keys.lastUnlock, 0)
|
||||
|
||||
fun secureScreen() = rxPrefs.getBoolean(Keys.secureScreen, false)
|
||||
|
||||
fun removeArticles() = rxPrefs.getBoolean(Keys.removeArticles, false)
|
||||
|
||||
fun migrateFlags() = rxPrefs.getInteger("migrate_flags", Int.MAX_VALUE)
|
||||
|
@ -5,6 +5,9 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import eu.kanade.tachiyomi.R
|
||||
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 uy.kohesive.injekt.injectLazy
|
||||
|
||||
@ -32,6 +35,12 @@ abstract class BaseActivity : AppCompatActivity() {
|
||||
else -> R.style.Theme_Tachiyomi
|
||||
})
|
||||
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
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import nucleus.view.NucleusAppCompatActivity
|
||||
|
||||
@ -11,4 +13,14 @@ abstract class BaseRxActivity<P : BasePresenter<*>> : NucleusAppCompatActivity<P
|
||||
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 androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.core.view.GravityCompat
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
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.recent_updates.RecentChaptersController
|
||||
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.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
@ -353,22 +353,10 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
||||
|
||||
override fun 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
|
||||
getExtensionUpdates()
|
||||
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() {
|
||||
@ -480,7 +468,7 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
||||
val baseController = router.backstack.last().controller() as? BaseController
|
||||
if (if (router.backstackSize == 1) !(baseController?.handleRootBack() ?: false)
|
||||
else !router.handleBack()) {
|
||||
unlocked = false
|
||||
SecureActivityDelegate.locked = true
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
@ -593,8 +581,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
||||
|
||||
private const val URL_HELP = "https://tachiyomi.org/help/"
|
||||
|
||||
var unlocked = false
|
||||
|
||||
var usingBottomNav = true
|
||||
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.withFadeTransaction
|
||||
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.view.updateLayoutParams
|
||||
import eu.kanade.tachiyomi.util.view.updatePadding
|
||||
@ -148,7 +149,7 @@ class SearchActivity: MainActivity() {
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (router.backstack.size <= 1 || !router.handleBack()) {
|
||||
unlocked = false
|
||||
SecureActivityDelegate.locked = true
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.ui.manga
|
||||
|
||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
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.info.MangaInfoController
|
||||
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 kotlinx.android.synthetic.main.main_activity.tabs
|
||||
import kotlinx.android.synthetic.main.manga_controller.manga_pager
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import kotlinx.android.synthetic.main.manga_controller.*
|
||||
import kotlinx.android.synthetic.main.search_activity.*
|
||||
import rx.Subscription
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
@ -98,6 +100,8 @@ class MangaController : RxController, TabbedController {
|
||||
|
||||
var startingChapterYPos:Float? = null
|
||||
|
||||
var isLockedFromSearch = false
|
||||
|
||||
private var adapter: MangaDetailAdapter? = null
|
||||
|
||||
val fromCatalogue = args.getBoolean(FROM_CATALOGUE_EXTRA, false)
|
||||
@ -131,6 +135,9 @@ class MangaController : RxController, TabbedController {
|
||||
manga_pager.offscreenPageLimit = 3
|
||||
manga_pager.adapter = adapter
|
||||
|
||||
isLockedFromSearch = activity is SearchActivity &&
|
||||
SecureActivityDelegate.shouldBeLocked()
|
||||
|
||||
if (!fromCatalogue)
|
||||
manga_pager.currentItem = CHAPTERS_CONTROLLER
|
||||
}
|
||||
@ -140,6 +147,12 @@ class MangaController : RxController, TabbedController {
|
||||
adapter = null
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
super.onActivityResumed(activity)
|
||||
isLockedFromSearch = activity is SearchActivity &&
|
||||
SecureActivityDelegate.shouldBeLocked()
|
||||
}
|
||||
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
super.onChangeStarted(handler, type)
|
||||
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.util.system.getResourceColor
|
||||
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.visible
|
||||
import kotlinx.android.synthetic.main.chapters_item.*
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
|
||||
class ChapterHolder(
|
||||
private val view: View,
|
||||
@ -26,7 +28,7 @@ class ChapterHolder(
|
||||
|
||||
fun bind(item: ChapterItem, manga: Manga) {
|
||||
val chapter = item.chapter
|
||||
|
||||
val isLocked = item.isLocked
|
||||
chapter_title.text = when (manga.displayMode) {
|
||||
Manga.DISPLAY_NUMBER -> {
|
||||
val number = adapter.decimalFormat.format(chapter.chapter_number.toDouble())
|
||||
@ -35,12 +37,16 @@ class ChapterHolder(
|
||||
else -> chapter.name
|
||||
}
|
||||
|
||||
chapter_menu.visible()
|
||||
// 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))
|
||||
|
||||
if (isLocked) chapter_menu.invisible()
|
||||
|
||||
// Set correct text color
|
||||
chapter_title.setTextColor(if (chapter.read) adapter.readColor else adapter.unreadColor)
|
||||
if (chapter.bookmark) chapter_title.setTextColor(adapter.bookmarkedColor)
|
||||
chapter_title.setTextColor(if (chapter.read && !isLocked)
|
||||
adapter.readColor else adapter.unreadColor)
|
||||
if (chapter.bookmark && !isLocked) chapter_title.setTextColor(adapter.bookmarkedColor)
|
||||
|
||||
if (chapter.date_upload > 0) {
|
||||
chapter_date.text = adapter.dateFormat.format(Date(chapter.date_upload))
|
||||
@ -59,7 +65,7 @@ class ChapterHolder(
|
||||
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)
|
||||
} else {
|
||||
""
|
||||
@ -81,6 +87,10 @@ class ChapterHolder(
|
||||
private fun showPopupMenu(view: View) {
|
||||
val item = adapter.getItem(adapterPosition) ?: return
|
||||
|
||||
if (item.isLocked) {
|
||||
adapter.unlock()
|
||||
return
|
||||
}
|
||||
// Create a PopupMenu, giving it the clicked view for an anchor
|
||||
val popup = PopupMenu(view.context, view)
|
||||
|
||||
|
@ -14,6 +14,7 @@ class ChapterItem(val chapter: Chapter, val manga: Manga) : AbstractFlexibleItem
|
||||
Chapter by chapter {
|
||||
|
||||
private var _status: Int = 0
|
||||
var isLocked = false
|
||||
|
||||
var status: Int
|
||||
get() = download?.status ?: _status
|
||||
|
@ -2,18 +2,20 @@ package eu.kanade.tachiyomi.ui.manga.chapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.MenuItem
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
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 uy.kohesive.injekt.injectLazy
|
||||
import java.text.DateFormat
|
||||
import java.text.DecimalFormat
|
||||
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(
|
||||
controller: ChaptersController,
|
||||
val controller: ChaptersController,
|
||||
context: Context
|
||||
) : FlexibleAdapter<ChapterItem>(null, controller, true) {
|
||||
|
||||
@ -43,8 +45,12 @@ class ChaptersAdapter(
|
||||
return items.indexOf(item)
|
||||
}
|
||||
|
||||
fun unlock() {
|
||||
val activity = controller.activity as? FragmentActivity ?: return
|
||||
SecureActivityDelegate.promptLockIfNeeded(activity)
|
||||
}
|
||||
|
||||
interface OnMenuItemClickListener {
|
||||
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.Manga
|
||||
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.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
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.view.doOnApplyWindowInsets
|
||||
import eu.kanade.tachiyomi.util.view.getCoordinates
|
||||
@ -95,6 +95,7 @@ class ChaptersController() : NucleusController<ChaptersPresenter>(),
|
||||
|
||||
// Init RecyclerView and adapter
|
||||
adapter = ChaptersAdapter(this, view.context)
|
||||
setReadingDrawable()
|
||||
|
||||
recycler.adapter = adapter
|
||||
recycler.layoutManager = LinearLayoutManager(view.context)
|
||||
@ -117,6 +118,10 @@ class ChaptersController() : NucleusController<ChaptersPresenter>(),
|
||||
swipe_refresh.refreshes().subscribeUntilDestroy { fetchChaptersFromSource() }
|
||||
|
||||
fab.clicks().subscribeUntilDestroy {
|
||||
if (activity is SearchActivity && presenter.isLockedFromSearch) {
|
||||
SecureActivityDelegate.promptLockIfNeeded(activity)
|
||||
return@subscribeUntilDestroy
|
||||
}
|
||||
val item = presenter.getNextUnreadChapter()
|
||||
if (item != null) {
|
||||
// Create animation listener
|
||||
@ -149,9 +154,29 @@ class ChaptersController() : NucleusController<ChaptersPresenter>(),
|
||||
actionMode = null
|
||||
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) {
|
||||
super.onActivityResumed(activity)
|
||||
if (view == null) return
|
||||
if (activity is SearchActivity) {
|
||||
presenter.updateLockStatus()
|
||||
setReadingDrawable()
|
||||
}
|
||||
|
||||
// Check if animation view is visible
|
||||
if (reveal_view.visibility == View.VISIBLE) {
|
||||
@ -159,11 +184,11 @@ class ChaptersController() : NucleusController<ChaptersPresenter>(),
|
||||
val coordinates = fab.getCoordinates()
|
||||
reveal_view.hideRevealEffect(coordinates.x, coordinates.y, 1920)
|
||||
}
|
||||
super.onActivityResumed(activity)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.chapters, menu)
|
||||
if (!(parentController as MangaController).isLockedFromSearch)
|
||||
inflater.inflate(R.menu.chapters, menu)
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
|
@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
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.lang.isNullOrUnsubscribed
|
||||
import rx.Observable
|
||||
@ -65,14 +66,28 @@ class ChaptersPresenter(
|
||||
*/
|
||||
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?) {
|
||||
super.onCreate(savedState)
|
||||
isLockedFromSearch = SecureActivityDelegate.shouldBeLocked()
|
||||
|
||||
// Prepare the relay.
|
||||
chaptersRelay.flatMap { applyChapterFilters(it) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeLatestCache(ChaptersController::onNextChapters,
|
||||
{ _, error -> Timber.e(error) })
|
||||
.subscribeLatestCache(ChaptersController::onNextChapters
|
||||
) { _, error -> Timber.e(error) }
|
||||
|
||||
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
|
||||
// changes, and sends the list of chapters to the relay.
|
||||
@ -120,6 +135,7 @@ class ChaptersPresenter(
|
||||
private fun Chapter.toModel(): ChapterItem {
|
||||
// Create the model object.
|
||||
val model = ChapterItem(this, manga)
|
||||
model.isLocked = isLockedFromSearch
|
||||
|
||||
// Find an active download for this chapter.
|
||||
val download = downloadManager.queue.find { it.chapter.id == id }
|
||||
|
@ -4,6 +4,7 @@ import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.app.PendingIntent
|
||||
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.LibraryController
|
||||
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.security.SecureActivityDelegate
|
||||
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.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
|
||||
import eu.kanade.tachiyomi.util.view.marginBottom
|
||||
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.updatePaddingRelative
|
||||
import jp.wasabeef.glide.transformations.CropSquareTransformation
|
||||
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.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 java.io.File
|
||||
import java.text.DateFormat
|
||||
@ -238,7 +232,8 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
||||
inflater.inflate(R.menu.manga_info, menu)
|
||||
|
||||
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 {
|
||||
@ -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) {
|
||||
manga_genres_tags.setOnTagClickListener(null)
|
||||
snack?.dismiss()
|
||||
@ -466,10 +466,13 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
||||
private fun setFavoriteDrawable(isFavorite: Boolean) {
|
||||
// Set the Favorite drawable to the correct one.
|
||||
// Border drawable if false, filled drawable if true.
|
||||
fab_favorite?.setImageResource(if (isFavorite)
|
||||
R.drawable.ic_bookmark_white_24dp
|
||||
else
|
||||
R.drawable.ic_add_to_library_24dp)
|
||||
fab_favorite?.setImageResource(
|
||||
when {
|
||||
(parentController as MangaController).isLockedFromSearch -> R.drawable.ic_lock_white_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.
|
||||
*/
|
||||
private fun onFabClick() {
|
||||
if ((parentController as MangaController).isLockedFromSearch) {
|
||||
SecureActivityDelegate.promptLockIfNeeded(activity)
|
||||
return
|
||||
}
|
||||
val manga = presenter.manga
|
||||
toggleFavorite()
|
||||
if (manga.favorite) {
|
||||
@ -689,6 +696,8 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
|
||||
* @param query the search query to pass to the search controller
|
||||
*/
|
||||
private fun performGlobalSearch(query: String) {
|
||||
if ((parentController as MangaController).isLockedFromSearch)
|
||||
return
|
||||
val router = parentController?.router ?: return
|
||||
router.pushController(CatalogueSearchController(query).withFadeTransaction())
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.track
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
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.ui.base.controller.NucleusController
|
||||
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.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 timber.log.Timber
|
||||
|
||||
@ -41,13 +46,31 @@ class TrackController : NucleusController<TrackPresenter>(),
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
if ((parentController as MangaController).isLockedFromSearch) {
|
||||
swipe_refresh.invisible()
|
||||
unlock_button.visible()
|
||||
unlock_button.setOnClickListener {
|
||||
SecureActivityDelegate.promptLockIfNeeded(activity)
|
||||
}
|
||||
}
|
||||
|
||||
adapter = TrackAdapter(this)
|
||||
with(view) {
|
||||
track_recycler.layoutManager = LinearLayoutManager(context)
|
||||
track_recycler.adapter = adapter
|
||||
track_recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
|
||||
swipe_refresh.isEnabled = false
|
||||
swipe_refresh.refreshes().subscribeUntilDestroy { presenter.refresh() }
|
||||
track_recycler.layoutManager = LinearLayoutManager(view.context)
|
||||
track_recycler.adapter = adapter
|
||||
track_recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
|
||||
swipe_refresh.isEnabled = false
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ import android.view.animation.Animation
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.SeekBar
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.biometric.BiometricManager
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
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.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
|
||||
@ -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.VerticalPagerViewer
|
||||
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.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||
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.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
@ -67,7 +65,6 @@ 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
|
||||
import kotlin.math.abs
|
||||
|
||||
@ -527,23 +524,6 @@ 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.
|
||||
|
@ -1,16 +1,15 @@
|
||||
package eu.kanade.tachiyomi.ui.main
|
||||
package eu.kanade.tachiyomi.ui.security
|
||||
|
||||
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.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class BiometricActivity : BaseActivity() {
|
||||
val executor = Executors.newSingleThreadExecutor()
|
||||
private val executor: ExecutorService = Executors.newSingleThreadExecutor()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -24,15 +23,10 @@ class BiometricActivity : BaseActivity() {
|
||||
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
super.onAuthenticationSucceeded(result)
|
||||
MainActivity.unlocked = true
|
||||
SecureActivityDelegate.locked = false
|
||||
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()
|
@ -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
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
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.ui.main.MainActivity
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import eu.kanade.tachiyomi.widget.preference.IntListMatPreference
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
||||
|
||||
class SettingsGeneralController : SettingsController() {
|
||||
@ -97,35 +94,53 @@ class SettingsGeneralController : SettingsController() {
|
||||
}
|
||||
}
|
||||
|
||||
val biometricManager = BiometricManager.from(context)
|
||||
if (biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
|
||||
var preference:IntListMatPreference? = null
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_security
|
||||
|
||||
val biometricManager = BiometricManager.from(context)
|
||||
if (biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
|
||||
var preference: IntListMatPreference? = null
|
||||
switchPreference {
|
||||
key = Keys.useBiometrics
|
||||
titleRes = R.string.lock_with_biometrics
|
||||
defaultValue = false
|
||||
|
||||
onChange {
|
||||
preference?.isVisible = it as Boolean
|
||||
true
|
||||
}
|
||||
}
|
||||
preference = intListPreference(activity) {
|
||||
key = Keys.lockAfter
|
||||
titleRes = R.string.lock_when_idle
|
||||
isVisible = preferences.useBiometrics().getOrDefault()
|
||||
val values = listOf(0, 2, 5, 10, 20, 30, 60, 90, 120, -1)
|
||||
entries = values.mapNotNull {
|
||||
when (it) {
|
||||
0 -> context.getString(R.string.lock_always)
|
||||
-1 -> context.getString(R.string.lock_never)
|
||||
else -> resources?.getQuantityString(
|
||||
R.plurals.lock_after_mins, it.toInt(), it
|
||||
)
|
||||
}
|
||||
}
|
||||
entryValues = values
|
||||
defaultValue = 0
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
key = Keys.useBiometrics
|
||||
titleRes = R.string.lock_with_biometrics
|
||||
key = Keys.secureScreen
|
||||
titleRes = R.string.pref_secure_screen
|
||||
summaryRes = R.string.pref_secure_screen_summary
|
||||
defaultValue = false
|
||||
|
||||
onChange {
|
||||
preference?.isVisible = it as Boolean
|
||||
it as Boolean
|
||||
SecureActivityDelegate.setSecure(activity, it)
|
||||
true
|
||||
}
|
||||
}
|
||||
preference = intListPreference(activity) {
|
||||
key = Keys.lockAfter
|
||||
titleRes = R.string.lock_when_idle
|
||||
isVisible = preferences.useBiometrics().getOrDefault()
|
||||
val values = listOf(0, 2, 5, 10, 20, 30, 60, 90, 120, -1)
|
||||
entries = values.mapNotNull {
|
||||
when (it) {
|
||||
0 -> context.getString(R.string.lock_always)
|
||||
-1 -> context.getString(R.string.lock_never)
|
||||
else -> resources?.getQuantityString(R.plurals.lock_after_mins, it.toInt(),
|
||||
it)
|
||||
}
|
||||
}
|
||||
entryValues = values
|
||||
defaultValue = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +185,8 @@ class SettingsLibraryController : SettingsController() {
|
||||
intListPreference(activity) {
|
||||
titleRes = R.string.pref_keep_category_sorting
|
||||
key = Keys.keepCatSort
|
||||
summaryRes = R.string.pref_keep_category_sorting_summary
|
||||
|
||||
customSummary = context.getString(R.string.pref_keep_category_sorting_summary)
|
||||
entries = listOf(
|
||||
context.getString(R.string.always_ask),
|
||||
context.getString(R.string.option_keep_category_sort),
|
||||
|
@ -27,7 +27,8 @@ AttributeSet? =
|
||||
defValue = defaultValue as? Int ?: defValue
|
||||
}
|
||||
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())
|
||||
return if (entries.isEmpty() || index == -1) ""
|
||||
else entries[index]
|
||||
|
@ -3,17 +3,11 @@ package eu.kanade.tachiyomi.widget.preference
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.AttributeSet
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
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,
|
||||
attrs: AttributeSet? =
|
||||
@ -34,6 +28,7 @@ open class ListMatPreference @JvmOverloads constructor(activity: Activity?, cont
|
||||
defValue = defaultValue as? String ?: defValue
|
||||
}
|
||||
override fun getSummary(): CharSequence {
|
||||
if (customSummary != null) return customSummary!!
|
||||
val index = entryValues.indexOf(prefs.getStringPref(key, defValue).getOrDefault())
|
||||
return if (entries.isEmpty() || index == -1) ""
|
||||
else entries[index]
|
||||
|
@ -16,19 +16,10 @@ open class MatPreference @JvmOverloads constructor(val activity: Activity?, cont
|
||||
null) :
|
||||
Preference(context, attrs) {
|
||||
|
||||
protected var useCustomSummary = false
|
||||
protected val prefs: PreferencesHelper = Injekt.get()
|
||||
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() {
|
||||
if (!isShowing)
|
||||
dialog().apply {
|
||||
@ -37,6 +28,10 @@ open class MatPreference @JvmOverloads constructor(val activity: Activity?, cont
|
||||
isShowing = true
|
||||
}
|
||||
|
||||
override fun getSummary(): CharSequence {
|
||||
return customSummary ?: super.getSummary()
|
||||
}
|
||||
|
||||
open fun dialog(): MaterialDialog {
|
||||
return MaterialDialog(activity ?: context).apply {
|
||||
if (title != null)
|
||||
|
@ -20,10 +20,15 @@ class MultiListMatPreference @JvmOverloads constructor(activity: Activity?, cont
|
||||
var customSummaryRes:Int
|
||||
get() = 0
|
||||
set(value) { customSummary = context.getString(value) }
|
||||
var customSummary:String? = null
|
||||
|
||||
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")
|
||||
|
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"?>
|
||||
<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"
|
||||
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
|
||||
android:id="@+id/swipe_refresh"
|
||||
@ -20,4 +22,17 @@
|
||||
|
||||
</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="manga_only">Manga 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="sort_by_">Sort by: %1$s</string>
|
||||
@ -169,6 +170,9 @@
|
||||
<string name="pref_date_format">Date format</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_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 -->
|
||||
<string name="pref_category_library_display">Display</string>
|
||||
|
Loading…
Reference in New Issue
Block a user