2016-03-19 12:48:55 -04:00
|
|
|
package eu.kanade.tachiyomi
|
|
|
|
|
2022-04-15 13:43:54 -04:00
|
|
|
import android.annotation.SuppressLint
|
2021-04-28 08:32:00 -04:00
|
|
|
import android.app.ActivityManager
|
2016-03-19 12:48:55 -04:00
|
|
|
import android.app.Application
|
2021-04-30 22:36:54 -04:00
|
|
|
import android.app.PendingIntent
|
|
|
|
import android.content.BroadcastReceiver
|
2016-10-15 05:12:16 -04:00
|
|
|
import android.content.Context
|
2021-04-30 22:36:54 -04:00
|
|
|
import android.content.Intent
|
|
|
|
import android.content.IntentFilter
|
2020-06-07 15:47:42 -04:00
|
|
|
import android.os.Build
|
2022-04-14 18:15:47 -04:00
|
|
|
import android.os.Looper
|
2021-05-28 22:34:26 -04:00
|
|
|
import android.webkit.WebView
|
2021-07-24 11:14:25 -04:00
|
|
|
import androidx.appcompat.app.AppCompatDelegate
|
2021-04-30 22:36:54 -04:00
|
|
|
import androidx.core.app.NotificationManagerCompat
|
2021-04-28 08:32:00 -04:00
|
|
|
import androidx.core.content.getSystemService
|
2021-09-20 14:33:35 -04:00
|
|
|
import androidx.lifecycle.DefaultLifecycleObserver
|
|
|
|
import androidx.lifecycle.LifecycleOwner
|
2020-02-21 22:58:19 -05:00
|
|
|
import androidx.lifecycle.ProcessLifecycleOwner
|
2021-04-30 22:36:54 -04:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2021-04-28 08:32:00 -04:00
|
|
|
import coil.ImageLoader
|
|
|
|
import coil.ImageLoaderFactory
|
|
|
|
import coil.decode.GifDecoder
|
|
|
|
import coil.decode.ImageDecoderDecoder
|
2022-03-04 16:04:32 -05:00
|
|
|
import coil.disk.DiskCache
|
2021-10-07 22:12:55 -04:00
|
|
|
import coil.util.DebugLogger
|
2021-04-28 08:32:00 -04:00
|
|
|
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
|
2022-03-04 16:04:32 -05:00
|
|
|
import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
|
2021-07-10 15:44:34 -04:00
|
|
|
import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder
|
2017-10-10 08:15:41 -04:00
|
|
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
2021-07-24 11:14:25 -04:00
|
|
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
2020-02-21 22:58:19 -05:00
|
|
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
2021-04-28 08:32:00 -04:00
|
|
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
2022-04-02 09:54:21 -04:00
|
|
|
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
|
2021-12-31 16:32:24 -05:00
|
|
|
import eu.kanade.tachiyomi.util.preference.asImmediateFlow
|
2021-08-19 18:10:07 -04:00
|
|
|
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
|
2022-04-02 10:49:42 -04:00
|
|
|
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
2021-08-27 08:44:09 -04:00
|
|
|
import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
2022-04-22 12:34:53 -04:00
|
|
|
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
2021-11-07 09:11:41 -05:00
|
|
|
import eu.kanade.tachiyomi.util.system.logcat
|
2021-04-30 22:36:54 -04:00
|
|
|
import eu.kanade.tachiyomi.util.system.notification
|
|
|
|
import kotlinx.coroutines.flow.launchIn
|
|
|
|
import kotlinx.coroutines.flow.onEach
|
2021-10-07 22:12:55 -04:00
|
|
|
import logcat.AndroidLogcatLogger
|
|
|
|
import logcat.LogPriority
|
|
|
|
import logcat.LogcatLogger
|
2021-05-27 08:49:47 -04:00
|
|
|
import org.acra.config.httpSender
|
|
|
|
import org.acra.ktx.initAcra
|
2020-04-15 22:57:34 -04:00
|
|
|
import org.acra.sender.HttpSender
|
2020-06-07 15:47:42 -04:00
|
|
|
import org.conscrypt.Conscrypt
|
2016-06-14 09:13:48 -04:00
|
|
|
import uy.kohesive.injekt.Injekt
|
2021-04-28 08:32:00 -04:00
|
|
|
import uy.kohesive.injekt.api.get
|
2020-02-21 22:58:19 -05:00
|
|
|
import uy.kohesive.injekt.injectLazy
|
2020-09-13 18:48:20 -04:00
|
|
|
import java.security.Security
|
2016-03-19 12:48:55 -04:00
|
|
|
|
2021-09-20 14:33:35 -04:00
|
|
|
open class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
2016-03-19 12:48:55 -04:00
|
|
|
|
2021-01-17 11:09:29 -05:00
|
|
|
private val preferences: PreferencesHelper by injectLazy()
|
|
|
|
|
2021-04-30 22:36:54 -04:00
|
|
|
private val disableIncognitoReceiver = DisableIncognitoReceiver()
|
|
|
|
|
2022-04-15 13:43:54 -04:00
|
|
|
@SuppressLint("LaunchActivityFromNotification")
|
2016-03-19 12:48:55 -04:00
|
|
|
override fun onCreate() {
|
2021-09-20 14:33:35 -04:00
|
|
|
super<Application>.onCreate()
|
2018-01-09 06:27:45 -05:00
|
|
|
|
2020-07-04 10:27:57 -04:00
|
|
|
// TLS 1.3 support for Android < 10
|
2020-06-07 15:47:42 -04:00
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
|
|
Security.insertProviderAt(Conscrypt.newProvider(), 1)
|
|
|
|
}
|
|
|
|
|
2021-05-28 22:34:26 -04:00
|
|
|
// Avoid potential crashes
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
|
|
val process = getProcessName()
|
|
|
|
if (packageName != process) WebView.setDataDirectorySuffix(process)
|
|
|
|
}
|
|
|
|
|
2016-06-14 09:13:48 -04:00
|
|
|
Injekt.importModule(AppModule(this))
|
2016-06-15 11:58:28 -04:00
|
|
|
|
2016-03-19 12:48:55 -04:00
|
|
|
setupAcra()
|
2017-10-10 08:15:41 -04:00
|
|
|
setupNotificationChannels()
|
2016-12-13 14:47:46 -05:00
|
|
|
|
2020-02-21 22:58:19 -05:00
|
|
|
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
2021-04-25 10:57:14 -04:00
|
|
|
|
2021-04-30 22:36:54 -04:00
|
|
|
// 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))
|
2021-06-09 17:16:09 -04:00
|
|
|
setSmallIcon(R.drawable.ic_glasses_24dp)
|
2021-04-30 22:36:54 -04:00
|
|
|
setOngoing(true)
|
|
|
|
|
|
|
|
val pendingIntent = PendingIntent.getBroadcast(
|
|
|
|
this@App,
|
|
|
|
0,
|
|
|
|
Intent(ACTION_DISABLE_INCOGNITO_MODE),
|
2022-04-08 16:44:23 -04:00
|
|
|
PendingIntent.FLAG_ONE_SHOT,
|
2021-04-30 22:36:54 -04:00
|
|
|
)
|
|
|
|
setContentIntent(pendingIntent)
|
|
|
|
}
|
|
|
|
notificationManager.notify(Notifications.ID_INCOGNITO_MODE, notification)
|
|
|
|
} else {
|
|
|
|
disableIncognitoReceiver.unregister()
|
|
|
|
notificationManager.cancel(Notifications.ID_INCOGNITO_MODE)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
|
2021-07-24 11:14:25 -04:00
|
|
|
|
|
|
|
preferences.themeMode()
|
|
|
|
.asImmediateFlow {
|
|
|
|
AppCompatDelegate.setDefaultNightMode(
|
|
|
|
when (it) {
|
|
|
|
PreferenceValues.ThemeMode.light -> AppCompatDelegate.MODE_NIGHT_NO
|
|
|
|
PreferenceValues.ThemeMode.dark -> AppCompatDelegate.MODE_NIGHT_YES
|
|
|
|
PreferenceValues.ThemeMode.system -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
2022-04-08 16:44:23 -04:00
|
|
|
},
|
2021-07-24 11:14:25 -04:00
|
|
|
)
|
|
|
|
}.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
|
2021-10-07 22:12:55 -04:00
|
|
|
|
|
|
|
if (!LogcatLogger.isInstalled && preferences.verboseLogging()) {
|
|
|
|
LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
|
|
|
|
}
|
2016-03-19 12:48:55 -04:00
|
|
|
}
|
|
|
|
|
2021-04-28 08:32:00 -04:00
|
|
|
override fun newImageLoader(): ImageLoader {
|
|
|
|
return ImageLoader.Builder(this).apply {
|
2022-03-04 16:04:32 -05:00
|
|
|
val callFactoryInit = { Injekt.get<NetworkHelper>().client }
|
|
|
|
val diskCacheInit = { CoilDiskCache.get(this@App) }
|
|
|
|
components {
|
2021-04-28 08:32:00 -04:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
2022-03-04 16:04:32 -05:00
|
|
|
add(ImageDecoderDecoder.Factory())
|
2021-04-28 08:32:00 -04:00
|
|
|
} else {
|
2022-03-04 16:04:32 -05:00
|
|
|
add(GifDecoder.Factory())
|
2021-04-28 08:32:00 -04:00
|
|
|
}
|
2022-03-04 16:04:32 -05:00
|
|
|
add(TachiyomiImageDecoder.Factory())
|
|
|
|
add(MangaCoverFetcher.Factory(lazy(callFactoryInit), lazy(diskCacheInit)))
|
|
|
|
add(MangaCoverKeyer())
|
2021-04-28 08:32:00 -04:00
|
|
|
}
|
2022-03-04 16:04:32 -05:00
|
|
|
callFactory(callFactoryInit)
|
|
|
|
diskCache(diskCacheInit)
|
2021-08-27 08:44:09 -04:00
|
|
|
crossfade((300 * this@App.animatorDurationScale).toInt())
|
2021-04-28 08:32:00 -04:00
|
|
|
allowRgb565(getSystemService<ActivityManager>()!!.isLowRamDevice)
|
2021-10-07 22:12:55 -04:00
|
|
|
if (preferences.verboseLogging()) logger(DebugLogger())
|
2021-04-28 08:32:00 -04:00
|
|
|
}.build()
|
|
|
|
}
|
|
|
|
|
2021-09-20 14:33:35 -04:00
|
|
|
override fun onStop(owner: LifecycleOwner) {
|
2021-08-19 18:10:07 -04:00
|
|
|
if (!AuthenticatorUtil.isAuthenticating && preferences.lockAppAfter().get() >= 0) {
|
2020-02-22 13:30:36 -05:00
|
|
|
SecureActivityDelegate.locked = true
|
2020-02-21 22:58:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-02 10:49:42 -04:00
|
|
|
override fun getPackageName(): String {
|
2022-04-15 13:43:54 -04:00
|
|
|
// 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 {
|
2022-04-15 15:52:48 -04:00
|
|
|
it.className.equals(
|
|
|
|
"org.chromium.base.BuildInfo",
|
|
|
|
ignoreCase = true,
|
|
|
|
)
|
2022-04-15 13:43:54 -04:00
|
|
|
}
|
|
|
|
if (chromiumElement?.methodName.equals("getAll", ignoreCase = true)) {
|
|
|
|
return WebViewUtil.SPOOF_PACKAGE_NAME
|
|
|
|
}
|
|
|
|
} catch (e: Exception) {
|
2022-04-02 10:49:42 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return super.getPackageName()
|
|
|
|
}
|
|
|
|
|
2016-03-19 12:48:55 -04:00
|
|
|
protected open fun setupAcra() {
|
2022-04-22 12:34:53 -04:00
|
|
|
if (isDevFlavor.not()) {
|
2021-05-27 08:49:47 -04:00
|
|
|
initAcra {
|
|
|
|
buildConfigClass = BuildConfig::class.java
|
2022-04-11 08:56:22 -04:00
|
|
|
excludeMatchingSharedPreferencesKeys = listOf(".*username.*", ".*password.*", ".*token.*")
|
2021-05-27 08:49:47 -04:00
|
|
|
|
|
|
|
httpSender {
|
|
|
|
uri = BuildConfig.ACRA_URI
|
|
|
|
httpMethod = HttpSender.Method.PUT
|
|
|
|
}
|
|
|
|
}
|
2021-01-27 09:17:40 -05:00
|
|
|
}
|
2016-03-19 12:48:55 -04:00
|
|
|
}
|
|
|
|
|
2017-10-10 08:15:41 -04:00
|
|
|
protected open fun setupNotificationChannels() {
|
2021-11-07 09:11:41 -05:00
|
|
|
try {
|
|
|
|
Notifications.createChannels(this)
|
|
|
|
} catch (e: Exception) {
|
|
|
|
logcat(LogPriority.ERROR, e) { "Failed to modify notification channels" }
|
|
|
|
}
|
2017-10-10 08:15:41 -04:00
|
|
|
}
|
2021-04-30 22:36:54 -04:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-19 12:48:55 -04:00
|
|
|
}
|
2021-09-05 14:34:29 -04:00
|
|
|
|
|
|
|
private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE"
|
2022-03-04 16:04:32 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Direct copy of Coil's internal SingletonDiskCache so that [MangaCoverFetcher] can access it.
|
|
|
|
*/
|
|
|
|
internal object CoilDiskCache {
|
|
|
|
|
|
|
|
private const val FOLDER_NAME = "image_cache"
|
|
|
|
private var instance: DiskCache? = null
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
fun get(context: Context): DiskCache {
|
|
|
|
return instance ?: run {
|
|
|
|
val safeCacheDir = context.cacheDir.apply { mkdirs() }
|
|
|
|
// Create the singleton disk cache instance.
|
|
|
|
DiskCache.Builder()
|
|
|
|
.directory(safeCacheDir.resolve(FOLDER_NAME))
|
|
|
|
.build()
|
|
|
|
.also { instance = it }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|