From 64c50c1283969dbc16c519f87cad3b5c9001b4cb Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 4 Nov 2023 19:20:48 -0400 Subject: [PATCH] Require Android 8+ Given that the next stable version of Chrome (120) will require Android 8+, it's inevitable that the WebView functionality will gradually break. As always, newer OS versions are recommended for better support with evolving Internet technologies. According to https://apilevels.com/, Android 8+ still covers 93.7% of Android users. --- .../java/eu/kanade/domain/ui/UiPreferences.kt | 6 +- .../manga/components/MangaCoverDialog.kt | 8 +- .../settings/screen/SettingsAdvancedScreen.kt | 101 ++++++++---------- .../screen/SettingsAppearanceScreen.kt | 18 +--- .../settings/screen/SettingsReaderScreen.kt | 1 - .../eu/kanade/presentation/util/Navigator.kt | 1 + app/src/main/java/eu/kanade/tachiyomi/App.kt | 27 +++-- .../data/coil/TachiyomiImageDecoder.kt | 4 +- .../data/notification/NotificationReceiver.kt | 23 ++-- .../extension/installer/ShizukuInstaller.kt | 7 +- .../tachiyomi/util/storage/FileExtensions.kt | 8 +- .../util/system/NetworkExtensions.kt | 3 +- buildSrc/src/main/kotlin/AndroidConfig.kt | 2 +- .../util/system/WebViewClientCompat.kt | 3 - .../tachiyomi/util/system/WebViewUtil.kt | 15 +-- 15 files changed, 84 insertions(+), 143 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt index 294812bdc..efb27fa02 100644 --- a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt @@ -1,6 +1,5 @@ package eu.kanade.domain.ui -import android.os.Build import eu.kanade.domain.ui.model.AppTheme import eu.kanade.domain.ui.model.TabletUiMode import eu.kanade.domain.ui.model.ThemeMode @@ -16,10 +15,7 @@ class UiPreferences( private val preferenceStore: PreferenceStore, ) { - fun themeMode() = preferenceStore.getEnum( - "pref_theme_mode_key", - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ThemeMode.SYSTEM } else { ThemeMode.LIGHT }, - ) + fun themeMode() = preferenceStore.getEnum("pref_theme_mode_key", ThemeMode.SYSTEM) fun appTheme() = preferenceStore.getEnum( "pref_app_theme", diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt index 43e9ac158..21057a634 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt @@ -2,7 +2,6 @@ package eu.kanade.presentation.manga.components import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable -import android.os.Build import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row @@ -176,14 +175,9 @@ fun MangaCoverDialog( // Copy bitmap in case it came from memory cache // Because SSIV needs to thoroughly read the image val copy = (drawable as? BitmapDrawable)?.let { - val config = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Bitmap.Config.HARDWARE - } else { - Bitmap.Config.ARGB_8888 - } BitmapDrawable( view.context.resources, - it.bitmap.copy(config, false), + it.bitmap.copy(Bitmap.Config.HARDWARE, false), ) } ?: drawable view.setImage(copy, ReaderPageImageView.Config(zoomDuration = 500)) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index 97d852e50..6b8cc1454 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -3,7 +3,6 @@ package eu.kanade.presentation.more.settings.screen import android.annotation.SuppressLint import android.content.ActivityNotFoundException import android.content.Intent -import android.os.Build import android.provider.Settings import android.webkit.WebStorage import android.webkit.WebView @@ -81,62 +80,50 @@ object SettingsAdvancedScreen : SearchableSettings { val basePreferences = remember { Injekt.get() } val networkPreferences = remember { Injekt.get() } - return buildList { - addAll( - listOf( - Preference.PreferenceItem.SwitchPreference( - pref = basePreferences.acraEnabled(), - title = stringResource(R.string.pref_enable_acra), - subtitle = stringResource(R.string.pref_acra_summary), - enabled = isPreviewBuildType || isReleaseBuildType, - ), - Preference.PreferenceItem.TextPreference( - title = stringResource(R.string.pref_dump_crash_logs), - subtitle = stringResource(R.string.pref_dump_crash_logs_summary), - onClick = { - scope.launch { - CrashLogUtil(context).dumpLogs() - } - }, - ), - Preference.PreferenceItem.SwitchPreference( - pref = networkPreferences.verboseLogging(), - title = stringResource(R.string.pref_verbose_logging), - subtitle = stringResource(R.string.pref_verbose_logging_summary), - onValueChanged = { - context.toast(R.string.requires_app_restart) - true - }, - ), - Preference.PreferenceItem.TextPreference( - title = stringResource(R.string.pref_debug_info), - onClick = { navigator.push(DebugInfoScreen()) }, - ), - ), - ) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - add( - Preference.PreferenceItem.TextPreference( - title = stringResource(R.string.pref_manage_notifications), - onClick = { - val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { - putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) - } - context.startActivity(intent) - }, - ), - ) - } - addAll( - listOf( - getBackgroundActivityGroup(), - getDataGroup(), - getNetworkGroup(networkPreferences = networkPreferences), - getLibraryGroup(), - getExtensionsGroup(basePreferences = basePreferences), - ), - ) - } + return listOf( + Preference.PreferenceItem.SwitchPreference( + pref = basePreferences.acraEnabled(), + title = stringResource(R.string.pref_enable_acra), + subtitle = stringResource(R.string.pref_acra_summary), + enabled = isPreviewBuildType || isReleaseBuildType, + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.pref_dump_crash_logs), + subtitle = stringResource(R.string.pref_dump_crash_logs_summary), + onClick = { + scope.launch { + CrashLogUtil(context).dumpLogs() + } + }, + ), + Preference.PreferenceItem.SwitchPreference( + pref = networkPreferences.verboseLogging(), + title = stringResource(R.string.pref_verbose_logging), + subtitle = stringResource(R.string.pref_verbose_logging_summary), + onValueChanged = { + context.toast(R.string.requires_app_restart) + true + }, + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.pref_debug_info), + onClick = { navigator.push(DebugInfoScreen()) }, + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.pref_manage_notifications), + onClick = { + val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { + putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) + } + context.startActivity(intent) + }, + ), + getBackgroundActivityGroup(), + getDataGroup(), + getNetworkGroup(networkPreferences = networkPreferences), + getLibraryGroup(), + getExtensionsGroup(basePreferences = basePreferences), + ) } @Composable diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 4540aee95..0ee26274f 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -2,7 +2,6 @@ package eu.kanade.presentation.more.settings.screen import android.app.Activity import android.content.Context -import android.os.Build import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatDelegate import androidx.compose.runtime.Composable @@ -81,18 +80,11 @@ object SettingsAppearanceScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = themeModePref, title = stringResource(R.string.pref_theme_mode), - entries = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mapOf( - ThemeMode.SYSTEM to stringResource(R.string.theme_system), - ThemeMode.LIGHT to stringResource(R.string.theme_light), - ThemeMode.DARK to stringResource(R.string.theme_dark), - ) - } else { - mapOf( - ThemeMode.LIGHT to stringResource(R.string.theme_light), - ThemeMode.DARK to stringResource(R.string.theme_dark), - ) - }, + entries = mapOf( + ThemeMode.SYSTEM to stringResource(R.string.theme_system), + ThemeMode.LIGHT to stringResource(R.string.theme_light), + ThemeMode.DARK to stringResource(R.string.theme_dark), + ), ), Preference.PreferenceItem.CustomPreference( title = stringResource(R.string.pref_app_theme), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt index 14e0fd9df..0f343aa3d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -58,7 +58,6 @@ object SettingsReaderScreen : SearchableSettings { pref = readerPref.trueColor(), title = stringResource(R.string.pref_true_color), subtitle = stringResource(R.string.pref_true_color_summary), - enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O, ), Preference.PreferenceItem.SwitchPreference( pref = readerPref.pageTransitions(), diff --git a/app/src/main/java/eu/kanade/presentation/util/Navigator.kt b/app/src/main/java/eu/kanade/presentation/util/Navigator.kt index bcbf7110f..6b5fceda7 100644 --- a/app/src/main/java/eu/kanade/presentation/util/Navigator.kt +++ b/app/src/main/java/eu/kanade/presentation/util/Navigator.kt @@ -79,6 +79,7 @@ fun ScreenTransition( targetState = navigator.lastItem, transitionSpec = transition, modifier = modifier, + label = "ScreenTransition", ) { screen -> navigator.saveableState("transition", screen) { content(screen) diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 822c66b81..4323200dd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -171,22 +171,19 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { } override fun getPackageName(): String { - // This causes freezes in Android 6/7 for some reason - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - try { - // Override the value passed as X-Requested-With in WebView requests - val stackTrace = Looper.getMainLooper().thread.stackTrace - val chromiumElement = stackTrace.find { - it.className.equals( - "org.chromium.base.BuildInfo", - ignoreCase = true, - ) - } - if (chromiumElement?.methodName.equals("getAll", ignoreCase = true)) { - return WebViewUtil.SPOOF_PACKAGE_NAME - } - } catch (e: Exception) { + try { + // Override the value passed as X-Requested-With in WebView requests + val stackTrace = Looper.getMainLooper().thread.stackTrace + val chromiumElement = stackTrace.find { + it.className.equals( + "org.chromium.base.BuildInfo", + ignoreCase = true, + ) } + if (chromiumElement?.methodName.equals("getAll", ignoreCase = true)) { + return WebViewUtil.SPOOF_PACKAGE_NAME + } + } catch (e: Exception) { } return super.getPackageName() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index e0a5ffe8e..fb1278614 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.data.coil -import android.os.Build import androidx.core.graphics.drawable.toDrawable import coil.ImageLoader import coil.decode.DecodeResult @@ -48,8 +47,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti ImageUtil.findImageType(it) } return when (type) { - ImageUtil.ImageType.AVIF, ImageUtil.ImageType.JXL -> true - ImageUtil.ImageType.HEIF -> Build.VERSION.SDK_INT < Build.VERSION_CODES.O + ImageUtil.ImageType.AVIF, ImageUtil.ImageType.JXL, ImageUtil.ImageType.HEIF -> true else -> false } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt index 566da8e9c..22c19cab0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt @@ -5,7 +5,6 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.net.Uri -import android.os.Build import androidx.core.net.toUri import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.backup.BackupRestoreJob @@ -368,20 +367,18 @@ class NotificationReceiver : BroadcastReceiver() { When programmatically dismissing this notification, the group notification is not automatically dismissed. */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - val groupKey = context.notificationManager.activeNotifications.find { - it.id == notificationId - }?.groupKey + val groupKey = context.notificationManager.activeNotifications.find { + it.id == notificationId + }?.groupKey - if (groupId != null && groupId != 0 && groupKey != null && groupKey.isNotEmpty()) { - val notifications = context.notificationManager.activeNotifications.filter { - it.groupKey == groupKey - } + if (groupId != null && groupId != 0 && !groupKey.isNullOrEmpty()) { + val notifications = context.notificationManager.activeNotifications.filter { + it.groupKey == groupKey + } - if (notifications.size == 2) { - context.cancelNotification(groupId) - return - } + if (notifications.size == 2) { + context.cancelNotification(groupId) + return } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt index 6cc4efd33..0d785e3a6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.installer import android.app.Service import android.content.pm.PackageManager -import android.os.Build import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.util.system.getUriSize @@ -50,11 +49,7 @@ class ShizukuInstaller(private val service: Service) : Installer(service) { try { val size = service.getUriSize(entry.uri) ?: throw IllegalStateException() service.contentResolver.openInputStream(entry.uri)!!.use { - val createCommand = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - "pm install-create --user current -r -i ${service.packageName} -S $size" - } else { - "pm install-create -r -i ${service.packageName} -S $size" - } + val createCommand = "pm install-create --user current -r -i ${service.packageName} -S $size" val createResult = exec(createCommand) sessionId = SESSION_ID_REGEX.find(createResult.out)?.value ?: throw RuntimeException("Failed to create install session") diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/storage/FileExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/storage/FileExtensions.kt index 94a65ddcd..f86d36485 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/storage/FileExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/storage/FileExtensions.kt @@ -2,9 +2,7 @@ package eu.kanade.tachiyomi.util.storage import android.content.Context import android.net.Uri -import android.os.Build import androidx.core.content.FileProvider -import androidx.core.net.toUri import eu.kanade.tachiyomi.BuildConfig import java.io.File @@ -17,11 +15,7 @@ val Context.cacheImageDir: File * @param context context of application */ fun File.getUriCompat(context: Context): Uri { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", this) - } else { - this.toUri() - } + return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", this) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkExtensions.kt index 8d72a9c86..18e16baac 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkExtensions.kt @@ -18,8 +18,7 @@ fun Context.isOnline(): Boolean { val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false val maxTransport = when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> NetworkCapabilities.TRANSPORT_LOWPAN - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> NetworkCapabilities.TRANSPORT_WIFI_AWARE - else -> NetworkCapabilities.TRANSPORT_VPN + else -> NetworkCapabilities.TRANSPORT_WIFI_AWARE } return (NetworkCapabilities.TRANSPORT_CELLULAR..maxTransport).any(networkCapabilities::hasTransport) } diff --git a/buildSrc/src/main/kotlin/AndroidConfig.kt b/buildSrc/src/main/kotlin/AndroidConfig.kt index f63c07729..9a5cc2f6b 100644 --- a/buildSrc/src/main/kotlin/AndroidConfig.kt +++ b/buildSrc/src/main/kotlin/AndroidConfig.kt @@ -1,6 +1,6 @@ object AndroidConfig { const val compileSdk = 34 - const val minSdk = 23 + const val minSdk = 26 const val targetSdk = 29 const val ndk = "22.1.7171670" } diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt b/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt index 93ce0337d..6547a46e0 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt +++ b/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt @@ -1,7 +1,5 @@ package eu.kanade.tachiyomi.util.system -import android.annotation.TargetApi -import android.os.Build import android.webkit.WebResourceError import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse @@ -28,7 +26,6 @@ abstract class WebViewClientCompat : WebViewClient() { ) { } - @TargetApi(Build.VERSION_CODES.N) final override fun shouldOverrideUrlLoading( view: WebView, request: WebResourceRequest, diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt b/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt index b3329e9b7..b60bc8dc0 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt +++ b/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt @@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.util.system import android.annotation.SuppressLint import android.content.Context import android.content.pm.PackageManager -import android.os.Build import android.webkit.CookieManager import android.webkit.WebSettings import android.webkit.WebView @@ -35,15 +34,11 @@ object WebViewUtil { } fun getVersion(context: Context): String { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val webView = WebView.getCurrentWebViewPackage() ?: return "how did you get here?" - val pm = context.packageManager - val label = webView.applicationInfo.loadLabel(pm) - val version = webView.versionName - "$label $version" - } else { - "Unknown" - } + val webView = WebView.getCurrentWebViewPackage() ?: return "how did you get here?" + val pm = context.packageManager + val label = webView.applicationInfo.loadLabel(pm) + val version = webView.versionName + return "$label $version" } fun supportsWebView(context: Context): Boolean {