Option to enable exit confirmation
This commit is contained in:
@@ -130,6 +130,9 @@ class AppSettings(context: Context) {
|
|||||||
get() = prefs.getBoolean(KEY_PROTECT_APP_BIOMETRIC, true)
|
get() = prefs.getBoolean(KEY_PROTECT_APP_BIOMETRIC, true)
|
||||||
set(value) = prefs.edit { putBoolean(KEY_PROTECT_APP_BIOMETRIC, value) }
|
set(value) = prefs.edit { putBoolean(KEY_PROTECT_APP_BIOMETRIC, value) }
|
||||||
|
|
||||||
|
val isExitConfirmationEnabled: Boolean
|
||||||
|
get() = prefs.getBoolean(KEY_EXIT_CONFIRM, false)
|
||||||
|
|
||||||
var sourcesOrder: List<String>
|
var sourcesOrder: List<String>
|
||||||
get() = prefs.getString(KEY_SOURCES_ORDER, null)
|
get() = prefs.getString(KEY_SOURCES_ORDER, null)
|
||||||
?.split('|')
|
?.split('|')
|
||||||
@@ -306,6 +309,7 @@ class AppSettings(context: Context) {
|
|||||||
const val KEY_DOWNLOADS_SLOWDOWN = "downloads_slowdown"
|
const val KEY_DOWNLOADS_SLOWDOWN = "downloads_slowdown"
|
||||||
const val KEY_ALL_FAVOURITES_VISIBLE = "all_favourites_visible"
|
const val KEY_ALL_FAVOURITES_VISIBLE = "all_favourites_visible"
|
||||||
const val KEY_DOH = "doh"
|
const val KEY_DOH = "doh"
|
||||||
|
const val KEY_EXIT_CONFIRM = "exit_confirm"
|
||||||
|
|
||||||
// About
|
// About
|
||||||
const val KEY_APP_UPDATE = "app_update"
|
const val KEY_APP_UPDATE = "app_update"
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package org.koitharu.kotatsu.main.ui
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.OnBackPressedCallback
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.android.ext.android.get
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||||
|
|
||||||
|
class ExitCallback(
|
||||||
|
private val activity: ComponentActivity,
|
||||||
|
private val snackbarHost: View,
|
||||||
|
) : OnBackPressedCallback(false) {
|
||||||
|
|
||||||
|
private var job: Job? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
observeSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
job?.cancel()
|
||||||
|
job = activity.lifecycleScope.launch {
|
||||||
|
resetExitConfirmation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun resetExitConfirmation() {
|
||||||
|
isEnabled = false
|
||||||
|
val snackbar = Snackbar.make(snackbarHost, R.string.confirm_exit, Snackbar.LENGTH_INDEFINITE)
|
||||||
|
snackbar.show()
|
||||||
|
delay(2000)
|
||||||
|
snackbar.dismiss()
|
||||||
|
isEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeSettings() {
|
||||||
|
activity.get<AppSettings>()
|
||||||
|
.observeAsFlow(AppSettings.KEY_EXIT_CONFIRM) { isExitConfirmationEnabled }
|
||||||
|
.flowOn(Dispatchers.Default)
|
||||||
|
.onEach { isEnabled = it }
|
||||||
|
.launchIn(activity.lifecycleScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import android.os.Bundle
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.MarginLayoutParams
|
import android.view.ViewGroup.MarginLayoutParams
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.ActivityResultCallback
|
import androidx.activity.result.ActivityResultCallback
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
@@ -21,8 +20,6 @@ import com.google.android.material.appbar.AppBarLayout.LayoutParams.*
|
|||||||
import com.google.android.material.navigation.NavigationBarView
|
import com.google.android.material.navigation.NavigationBarView
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
@@ -63,8 +60,6 @@ class MainActivity :
|
|||||||
View.OnFocusChangeListener,
|
View.OnFocusChangeListener,
|
||||||
SearchSuggestionListener, NavigationBarView.OnItemSelectedListener {
|
SearchSuggestionListener, NavigationBarView.OnItemSelectedListener {
|
||||||
|
|
||||||
private var isConfirmingExit: Boolean = false
|
|
||||||
|
|
||||||
private val viewModel by viewModel<MainViewModel>()
|
private val viewModel by viewModel<MainViewModel>()
|
||||||
private val searchSuggestionViewModel by viewModel<SearchSuggestionViewModel>()
|
private val searchSuggestionViewModel by viewModel<SearchSuggestionViewModel>()
|
||||||
private val voiceInputLauncher = registerForActivityResult(VoiceInputContract(), VoiceInputCallback())
|
private val voiceInputLauncher = registerForActivityResult(VoiceInputContract(), VoiceInputCallback())
|
||||||
@@ -99,6 +94,7 @@ class MainActivity :
|
|||||||
binding.navRail?.headerView?.setOnClickListener(this)
|
binding.navRail?.headerView?.setOnClickListener(this)
|
||||||
binding.searchView.isVoiceSearchEnabled = voiceInputLauncher.resolve(this, null) != null
|
binding.searchView.isVoiceSearchEnabled = voiceInputLauncher.resolve(this, null) != null
|
||||||
|
|
||||||
|
onBackPressedDispatcher.addCallback(ExitCallback(this, binding.container))
|
||||||
supportFragmentManager.findFragmentByTag(TAG_PRIMARY)?.let {
|
supportFragmentManager.findFragmentByTag(TAG_PRIMARY)?.let {
|
||||||
if (it is LibraryFragment) binding.fab?.show() else binding.fab?.hide()
|
if (it is LibraryFragment) binding.fab?.show() else binding.fab?.hide()
|
||||||
} ?: onNavigationItemSelected(navBar.selectedItemId)
|
} ?: onNavigationItemSelected(navBar.selectedItemId)
|
||||||
@@ -136,7 +132,6 @@ class MainActivity :
|
|||||||
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||||
runOnCommit { onSearchClosed() }
|
runOnCommit { onSearchClosed() }
|
||||||
}
|
}
|
||||||
shouldHandleExitConfirmation() -> lifecycleScope.launch { resetExitConfirmation() }
|
|
||||||
else -> super.onBackPressed()
|
else -> super.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,19 +148,6 @@ class MainActivity :
|
|||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun resetExitConfirmation() {
|
|
||||||
isConfirmingExit = true
|
|
||||||
val toast = Toast.makeText(this, R.string.confirm_exit, Toast.LENGTH_LONG)
|
|
||||||
toast.show()
|
|
||||||
delay(2000)
|
|
||||||
toast.cancel()
|
|
||||||
isConfirmingExit = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun shouldHandleExitConfirmation(): Boolean {
|
|
||||||
return !isConfirmingExit
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
when (v.id) {
|
when (v.id) {
|
||||||
R.id.fab -> viewModel.openLastReader()
|
R.id.fab -> viewModel.openLastReader()
|
||||||
|
|||||||
@@ -337,5 +337,7 @@
|
|||||||
<string name="changelog">Changelog</string>
|
<string name="changelog">Changelog</string>
|
||||||
<string name="explore">Explore</string>
|
<string name="explore">Explore</string>
|
||||||
<string name="tools">Tools</string>
|
<string name="tools">Tools</string>
|
||||||
<string name="confirm_exit">Press back again to exit</string>
|
<string name="confirm_exit">Press "Back" again to exit</string>
|
||||||
|
<string name="exit_confirmation_summary">Press "Back" twice to exit the app</string>
|
||||||
|
<string name="exit_confirmation">Exit confirmation</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -43,11 +43,17 @@
|
|||||||
android:valueTo="150"
|
android:valueTo="150"
|
||||||
app:defaultValue="100" />
|
app:defaultValue="100" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="exit_confirm"
|
||||||
|
android:summary="@string/exit_confirmation_summary"
|
||||||
|
android:title="@string/exit_confirmation"
|
||||||
|
app:allowDividerAbove="true" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:key="protect_app"
|
android:key="protect_app"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:summary="@string/protect_application_summary"
|
android:summary="@string/protect_application_summary"
|
||||||
android:title="@string/protect_application"
|
android:title="@string/protect_application" />
|
||||||
app:allowDividerAbove="true" />
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
Reference in New Issue
Block a user