diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index c8fefff86..7292b81b8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -2,14 +2,20 @@ package eu.kanade.tachiyomi import android.app.ActivityManager import android.app.Application +import android.app.PendingIntent +import android.content.BroadcastReceiver import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.content.res.Configuration import android.os.Build +import androidx.core.app.NotificationManagerCompat import androidx.core.content.getSystemService import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ProcessLifecycleOwner +import androidx.lifecycle.lifecycleScope import androidx.multidex.MultiDex import coil.ImageLoader import coil.ImageLoaderFactory @@ -22,6 +28,9 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate import eu.kanade.tachiyomi.util.system.LocaleHelper +import eu.kanade.tachiyomi.util.system.notification +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import org.acra.ACRA import org.acra.annotation.AcraCore import org.acra.annotation.AcraHttpSender @@ -45,6 +54,8 @@ open class App : Application(), LifecycleObserver, ImageLoaderFactory { private val preferences: PreferencesHelper by injectLazy() + private val disableIncognitoReceiver = DisableIncognitoReceiver() + override fun onCreate() { super.onCreate() if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) @@ -65,6 +76,34 @@ open class App : Application(), LifecycleObserver, ImageLoaderFactory { // Reset Incognito Mode on relaunch preferences.incognitoMode().set(false) + + // Show notification to disable Incognito Mode when it's enabled + preferences.incognitoMode().asFlow() + .onEach { enabled -> + val notificationManager = NotificationManagerCompat.from(this) + if (enabled) { + disableIncognitoReceiver.register() + val notification = notification(Notifications.CHANNEL_INCOGNITO_MODE) { + setContentTitle(getString(R.string.pref_incognito_mode)) + setContentText(getString(R.string.notification_incognito_text)) + setSmallIcon(R.drawable.ic_glasses_black_24dp) + setOngoing(true) + + val pendingIntent = PendingIntent.getBroadcast( + this@App, + 0, + Intent(ACTION_DISABLE_INCOGNITO_MODE), + PendingIntent.FLAG_ONE_SHOT + ) + setContentIntent(pendingIntent) + } + notificationManager.notify(Notifications.ID_INCOGNITO_MODE, notification) + } else { + disableIncognitoReceiver.unregister() + notificationManager.cancel(Notifications.ID_INCOGNITO_MODE) + } + } + .launchIn(ProcessLifecycleOwner.get().lifecycleScope) } override fun attachBaseContext(base: Context) { @@ -111,4 +150,30 @@ open class App : Application(), LifecycleObserver, ImageLoaderFactory { protected open fun setupNotificationChannels() { Notifications.createChannels(this) } + + private inner class DisableIncognitoReceiver : BroadcastReceiver() { + private var registered = false + + override fun onReceive(context: Context, intent: Intent) { + preferences.incognitoMode().set(false) + } + + fun register() { + if (!registered) { + registerReceiver(this, IntentFilter(ACTION_DISABLE_INCOGNITO_MODE)) + registered = true + } + } + + fun unregister() { + if (registered) { + unregisterReceiver(this) + registered = false + } + } + } + + companion object { + private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE" + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt index 97a4b2ef3..4a37b1243 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt @@ -68,6 +68,12 @@ object Notifications { const val CHANNEL_CRASH_LOGS = "crash_logs_channel" const val ID_CRASH_LOGS = -601 + /** + * Notification channel used for Incognito Mode + */ + const val CHANNEL_INCOGNITO_MODE = "incognito_mode_channel" + const val ID_INCOGNITO_MODE = -701 + private val deprecatedChannels = listOf( "downloader_channel", "backup_restore_complete_channel" @@ -154,6 +160,11 @@ object Notifications { CHANNEL_CRASH_LOGS, context.getString(R.string.channel_crash_logs), NotificationManager.IMPORTANCE_HIGH + ), + NotificationChannel( + CHANNEL_INCOGNITO_MODE, + context.getString(R.string.pref_incognito_mode), + NotificationManager.IMPORTANCE_LOW ) ).forEach(context.notificationManager::createNotificationChannel) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 1d8d93d55..4f8359d08 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -41,6 +41,7 @@ import eu.kanade.tachiyomi.ui.base.controller.TabbedController import eu.kanade.tachiyomi.ui.base.controller.ToolbarLiftOnScrollController import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.browse.BrowseController +import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController import eu.kanade.tachiyomi.ui.download.DownloadController import eu.kanade.tachiyomi.ui.library.LibraryController @@ -224,7 +225,17 @@ class MainActivity : BaseViewBindingActivity() { .launchIn(lifecycleScope) preferences.incognitoMode() - .asImmediateFlow { binding.incognitoMode.isVisible = it } + .asImmediateFlow { + binding.incognitoMode.isVisible = it + + // Close BrowseSourceController and its MangaController child when incognito mode is disabled + if (!it) { + val fg = router.backstack.last().controller() + if (fg is BrowseSourceController || fg is MangaController && fg.fromSource) { + router.popToRoot() + } + } + } .launchIn(lifecycleScope) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index c12d8aaa5..153672316 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -127,7 +127,7 @@ class MangaController : var source: Source? = null private set - private val fromSource = args.getBoolean(FROM_SOURCE_EXTRA, false) + val fromSource = args.getBoolean(FROM_SOURCE_EXTRA, false) private val preferences: PreferencesHelper by injectLazy() private val coverCache: CoverCache by injectLazy() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 1adb6bd9e..6b4d0cfd5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -167,6 +167,12 @@ class ReaderActivity : BaseRxActivity() window.decorView.setOnSystemUiVisibilityChangeListener { setMenuVisibility(menuVisible, animate = false) } + + // Finish when incognito mode is disabled + preferences.incognitoMode().asFlow() + .drop(1) + .onEach { if (!it) finish() } + .launchIn(lifecycleScope) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index a61c46afe..f6cee19c6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -144,6 +144,8 @@ class ReaderPresenter( hasTrackers = tracks.size > 0 } + private val incognitoMode = preferences.incognitoMode().get() + /** * Called when the presenter is created. It retrieves the saved active chapter if the process * was restored. @@ -375,7 +377,7 @@ class ReaderPresenter( // Save last page read and mark as read if needed selectedChapter.chapter.last_page_read = page.index - val shouldTrack = !preferences.incognitoMode().get() || hasTrackers + val shouldTrack = !incognitoMode || hasTrackers if (selectedChapter.pages?.lastIndex == page.index && shouldTrack) { selectedChapter.chapter.read = true updateTrackChapterRead(selectedChapter) @@ -430,7 +432,7 @@ class ReaderPresenter( * If incognito mode isn't on or has at least 1 tracker */ private fun saveChapterProgress(chapter: ReaderChapter) { - if (!preferences.incognitoMode().get() || hasTrackers) { + if (!incognitoMode || hasTrackers) { db.updateChapterProgress(chapter.chapter).asRxCompletable() .onErrorComplete() .subscribeOn(Schedulers.io()) @@ -442,7 +444,7 @@ class ReaderPresenter( * Saves this [chapter] last read history if incognito mode isn't on. */ private fun saveChapterHistory(chapter: ReaderChapter) { - if (!preferences.incognitoMode().get()) { + if (!incognitoMode) { val history = History.create(chapter.chapter).apply { last_read = Date().time } db.updateHistoryLastRead(history).asRxCompletable() .onErrorComplete() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bbce163f9..e480943b7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -467,6 +467,7 @@ Downloaded only Incognito mode Pauses reading history + Disable incognito mode Filters all manga in your library 1 remaining @@ -776,5 +777,4 @@ Previous page Next page -