mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
fix: Handle version check for AboutController
This commit is contained in:
parent
eba5aa1d2e
commit
e06b28a60e
4 changed files with 90 additions and 49 deletions
|
@ -117,6 +117,7 @@ import eu.kanade.tachiyomi.util.system.prepareSideNavContext
|
||||||
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.system.tryTakePersistableUriPermission
|
import eu.kanade.tachiyomi.util.system.tryTakePersistableUriPermission
|
||||||
|
import eu.kanade.tachiyomi.util.system.withUIContext
|
||||||
import eu.kanade.tachiyomi.util.view.BackHandlerControllerInterface
|
import eu.kanade.tachiyomi.util.view.BackHandlerControllerInterface
|
||||||
import eu.kanade.tachiyomi.util.view.backgroundColor
|
import eu.kanade.tachiyomi.util.view.backgroundColor
|
||||||
import eu.kanade.tachiyomi.util.view.blurBehindWindow
|
import eu.kanade.tachiyomi.util.view.blurBehindWindow
|
||||||
|
@ -136,12 +137,10 @@ import kotlin.collections.set
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import yokai.core.migration.Migrator
|
import yokai.core.migration.Migrator
|
||||||
import yokai.domain.base.BasePreferences
|
import yokai.domain.base.BasePreferences
|
||||||
|
@ -198,6 +197,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
|
||||||
dimenW to dimenH
|
dimenW to dimenH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("Create contract directly from Composable")
|
||||||
private val requestNotificationPermissionLauncher =
|
private val requestNotificationPermissionLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||||
if (!isGranted) {
|
if (!isGranted) {
|
||||||
|
@ -1001,7 +1001,6 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkForAppUpdates() {
|
private fun checkForAppUpdates() {
|
||||||
// FIXME: Show Compose version of NewUpdateDialog for AboutController
|
|
||||||
if (isUpdaterEnabled && router.backstack.lastOrNull()?.controller !is AboutController) {
|
if (isUpdaterEnabled && router.backstack.lastOrNull()?.controller !is AboutController) {
|
||||||
lifecycleScope.launchIO {
|
lifecycleScope.launchIO {
|
||||||
try {
|
try {
|
||||||
|
@ -1012,7 +1011,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
|
||||||
val isBeta = result.release.preRelease == true
|
val isBeta = result.release.preRelease == true
|
||||||
|
|
||||||
// Create confirmation window
|
// Create confirmation window
|
||||||
withContext(Dispatchers.Main) {
|
withUIContext {
|
||||||
showNotificationPermissionPrompt()
|
showNotificationPermissionPrompt()
|
||||||
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
||||||
AboutController.NewUpdateDialogController(body, url, isBeta).showDialog(router)
|
AboutController.NewUpdateDialogController(body, url, isBeta).showDialog(router)
|
||||||
|
|
|
@ -21,23 +21,18 @@ import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.compose.LifecycleEventEffect
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import dev.icerock.moko.resources.compose.stringResource
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
|
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
|
||||||
import yokai.i18n.MR
|
import yokai.i18n.MR
|
||||||
import yokai.util.lang.getString
|
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
|
||||||
import yokai.presentation.component.Gap
|
import yokai.presentation.component.Gap
|
||||||
import yokai.presentation.theme.Size
|
import yokai.presentation.theme.Size
|
||||||
|
|
||||||
|
@ -53,35 +48,26 @@ internal class PermissionStep : OnboardingStep {
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val lifecycleOwner = LocalLifecycleOwner.current
|
|
||||||
|
|
||||||
DisposableEffect(lifecycleOwner.lifecycle) {
|
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
|
||||||
val observer = object : DefaultLifecycleObserver {
|
installGranted =
|
||||||
override fun onResume(owner: LifecycleOwner) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
installGranted =
|
context.packageManager.canRequestPackageInstalls()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
} else {
|
||||||
context.packageManager.canRequestPackageInstalls()
|
@Suppress("DEPRECATION")
|
||||||
} else {
|
Settings.Secure.getInt(
|
||||||
@Suppress("DEPRECATION")
|
context.contentResolver,
|
||||||
Settings.Secure.getInt(
|
Settings.Secure.INSTALL_NON_MARKET_APPS
|
||||||
context.contentResolver,
|
) != 0
|
||||||
Settings.Secure.INSTALL_NON_MARKET_APPS
|
} || context.isShizukuInstalled
|
||||||
) != 0
|
notificationGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
} || context.isShizukuInstalled
|
context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) ==
|
||||||
notificationGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
PackageManager.PERMISSION_GRANTED
|
||||||
context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) ==
|
} else {
|
||||||
PackageManager.PERMISSION_GRANTED
|
true
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
batteryGranted = context.getSystemService<PowerManager>()!!
|
|
||||||
.isIgnoringBatteryOptimizations(context.packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lifecycleOwner.lifecycle.addObserver(observer)
|
|
||||||
onDispose {
|
|
||||||
lifecycleOwner.lifecycle.removeObserver(observer)
|
|
||||||
}
|
}
|
||||||
|
batteryGranted = context.getSystemService<PowerManager>()!!
|
||||||
|
.isIgnoringBatteryOptimizations(context.packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
|
|
@ -10,14 +10,17 @@ import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import com.google.android.material.textview.MaterialTextView
|
import com.google.android.material.textview.MaterialTextView
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
import dev.icerock.moko.resources.compose.stringResource
|
||||||
import eu.kanade.tachiyomi.data.updater.AppDownloadInstallJob
|
import eu.kanade.tachiyomi.data.updater.AppDownloadInstallJob
|
||||||
import eu.kanade.tachiyomi.ui.more.parseReleaseNotes
|
import eu.kanade.tachiyomi.ui.more.parseReleaseNotes
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
import kotlin.coroutines.resume
|
||||||
import yokai.domain.DialogHostState
|
import yokai.domain.DialogHostState
|
||||||
import yokai.i18n.MR
|
import yokai.i18n.MR
|
||||||
|
import android.R as AR
|
||||||
|
|
||||||
data class NewUpdateData(
|
data class NewUpdateData(
|
||||||
val body: String,
|
val body: String,
|
||||||
|
@ -89,3 +92,17 @@ private fun MarkdownText(text: String) {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun DialogHostState.awaitNotificationPermissionDeniedDialog(): Unit = dialog { cont ->
|
||||||
|
// cont.resume(Unit) so that new update dialog will be shown next
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { if (cont.isActive) cont.resume(Unit) },
|
||||||
|
title = { Text(text = stringResource(MR.strings.warning)) },
|
||||||
|
text = { Text(text = stringResource(MR.strings.allow_notifications_recommended)) },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { if (cont.isActive) cont.resume(Unit) }) {
|
||||||
|
Text(text = stringResource(AR.string.ok))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
@ -25,8 +27,11 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.compose.LifecycleEventEffect
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
|
import dev.icerock.moko.resources.StringResource
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
import dev.icerock.moko.resources.compose.stringResource
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.core.storage.preference.asDateFormat
|
import eu.kanade.tachiyomi.core.storage.preference.asDateFormat
|
||||||
|
@ -39,7 +44,9 @@ import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||||
import eu.kanade.tachiyomi.util.compose.LocalDialogHostState
|
import eu.kanade.tachiyomi.util.compose.LocalDialogHostState
|
||||||
import eu.kanade.tachiyomi.util.compose.currentOrThrow
|
import eu.kanade.tachiyomi.util.compose.currentOrThrow
|
||||||
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
||||||
|
import eu.kanade.tachiyomi.util.showNotificationPermissionPrompt
|
||||||
import eu.kanade.tachiyomi.util.system.isOnline
|
import eu.kanade.tachiyomi.util.system.isOnline
|
||||||
|
import eu.kanade.tachiyomi.util.system.launchIO
|
||||||
import eu.kanade.tachiyomi.util.system.localeContext
|
import eu.kanade.tachiyomi.util.system.localeContext
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.system.withUIContext
|
import eu.kanade.tachiyomi.util.system.withUIContext
|
||||||
|
@ -49,7 +56,8 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import yokai.domain.DialogHostState
|
import yokai.domain.DialogHostState
|
||||||
import yokai.i18n.MR
|
import yokai.i18n.MR
|
||||||
import yokai.presentation.component.preference.widget.TextPreferenceWidget
|
import yokai.presentation.component.preference.widget.TextPreferenceWidget
|
||||||
|
@ -70,11 +78,34 @@ class AboutScreen : Screen() {
|
||||||
val dialogHostState = LocalDialogHostState.currentOrThrow
|
val dialogHostState = LocalDialogHostState.currentOrThrow
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
|
|
||||||
|
val preferences = remember { Injekt.get<PreferencesHelper>() }
|
||||||
|
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
val preferences: PreferencesHelper by injectLazy()
|
val requestNotificationPermission = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||||
|
if (!isGranted) {
|
||||||
|
scope.launch { dialogHostState.awaitNotificationPermissionDeniedDialog() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
|
||||||
|
// FIXME: Move this to MainActivity once the app is fully migrated to Compose
|
||||||
|
scope.launchIO {
|
||||||
|
context.checkVersion(
|
||||||
|
dialogState = dialogHostState,
|
||||||
|
isUserPrompt = false,
|
||||||
|
notificationPrompt = {
|
||||||
|
context.showNotificationPermissionPrompt(
|
||||||
|
requestNotificationPermission,
|
||||||
|
false,
|
||||||
|
preferences,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val dateFormat by lazy { preferences.dateFormatRaw().get().asDateFormat() }
|
val dateFormat by lazy { preferences.dateFormatRaw().get().asDateFormat() }
|
||||||
|
|
||||||
SettingsScaffold(
|
SettingsScaffold(
|
||||||
|
@ -108,7 +139,7 @@ class AboutScreen : Screen() {
|
||||||
onPreferenceClick = {
|
onPreferenceClick = {
|
||||||
if (context.isOnline()) {
|
if (context.isOnline()) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
context.checkVersion(dialogHostState)
|
context.checkVersion(dialogHostState, true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.toast(MR.strings.no_network_connection)
|
context.toast(MR.strings.no_network_connection)
|
||||||
|
@ -192,16 +223,25 @@ class AboutScreen : Screen() {
|
||||||
else -> "Release ${BuildConfig.VERSION_NAME}"
|
else -> "Release ${BuildConfig.VERSION_NAME}"
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun Context.checkVersion(dialogState: DialogHostState) {
|
private fun Context.toastIfNotUserPrompt(message: StringResource, isUserPrompt: Boolean) {
|
||||||
|
toastIfNotUserPrompt(getString(message), isUserPrompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Context.toastIfNotUserPrompt(message: String?, isUserPrompt: Boolean) {
|
||||||
|
if (!isUserPrompt) return
|
||||||
|
toast(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun Context.checkVersion(dialogState: DialogHostState, isUserPrompt: Boolean, notificationPrompt: () -> Unit = {}) {
|
||||||
val updateChecker = AppUpdateChecker()
|
val updateChecker = AppUpdateChecker()
|
||||||
|
|
||||||
withUIContext { toast(MR.strings.searching_for_updates) }
|
withUIContext { toastIfNotUserPrompt(MR.strings.searching_for_updates, isUserPrompt) }
|
||||||
|
|
||||||
val result = try {
|
val result = try {
|
||||||
updateChecker.checkForUpdate(this, true)
|
updateChecker.checkForUpdate(this, isUserPrompt)
|
||||||
} catch (error: Exception) {
|
} catch (error: Exception) {
|
||||||
withUIContext {
|
withUIContext {
|
||||||
toast(error.message)
|
toastIfNotUserPrompt(error.message, isUserPrompt)
|
||||||
Logger.e(error) { "Couldn't check new update" }
|
Logger.e(error) { "Couldn't check new update" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,14 +255,13 @@ class AboutScreen : Screen() {
|
||||||
|
|
||||||
// Create confirmation window
|
// Create confirmation window
|
||||||
withUIContext {
|
withUIContext {
|
||||||
|
if (!isUserPrompt) { notificationPrompt() }
|
||||||
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
||||||
dialogState.awaitNewUpdateDialog(data)
|
dialogState.awaitNewUpdateDialog(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is AppUpdateResult.NoNewUpdate -> {
|
is AppUpdateResult.NoNewUpdate -> {
|
||||||
withUIContext {
|
withUIContext { toastIfNotUserPrompt(MR.strings.no_new_updates_available, isUserPrompt) }
|
||||||
toast(MR.strings.no_new_updates_available)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue