251 lines
8.7 KiB
Kotlin
251 lines
8.7 KiB
Kotlin
/*
|
|
* Copyright 2016 Hippo Seven
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.hippo.ehviewer
|
|
|
|
import android.app.Application
|
|
import android.content.Context
|
|
import android.os.StrictMode
|
|
import androidx.appcompat.app.AppCompatDelegate
|
|
import androidx.appcompat.content.res.AppCompatResources
|
|
import androidx.compose.runtime.Composer
|
|
import androidx.compose.runtime.tooling.ComposeStackTraceMode
|
|
import androidx.lifecycle.ProcessLifecycleOwner
|
|
import androidx.lifecycle.coroutineScope
|
|
import coil3.EventListener
|
|
import coil3.SingletonImageLoader
|
|
import coil3.asImage
|
|
import coil3.gif.AnimatedImageDecoder
|
|
import coil3.gif.GifDecoder
|
|
import coil3.network.ConnectivityChecker
|
|
import coil3.network.ktor3.KtorNetworkFetcherFactory
|
|
import coil3.request.ErrorResult
|
|
import coil3.request.ImageRequest
|
|
import coil3.request.allowRgb565
|
|
import coil3.request.crossfade
|
|
import coil3.serviceLoaderEnabled
|
|
import coil3.util.DebugLogger
|
|
import com.ehviewer.core.database.SearchDatabase
|
|
import com.ehviewer.core.database.roomDb
|
|
import com.ehviewer.core.files.deleteContent
|
|
import com.ehviewer.core.ui.util.initSETConnection
|
|
import com.ehviewer.core.util.isAtLeastO
|
|
import com.ehviewer.core.util.isAtLeastP
|
|
import com.ehviewer.core.util.isAtLeastS
|
|
import com.ehviewer.core.util.launchIO
|
|
import com.ehviewer.core.util.logcat
|
|
import com.ehviewer.core.util.withUIContext
|
|
import com.hippo.ehviewer.client.EhTagDatabase
|
|
import com.hippo.ehviewer.coil.AnimatedWebPDecoder
|
|
import com.hippo.ehviewer.coil.CropBorderInterceptor
|
|
import com.hippo.ehviewer.coil.DetectBorderInterceptor
|
|
import com.hippo.ehviewer.coil.DownloadThumbInterceptor
|
|
import com.hippo.ehviewer.coil.HardwareBitmapInterceptor
|
|
import com.hippo.ehviewer.coil.MapExtraInfoInterceptor
|
|
import com.hippo.ehviewer.coil.MergeInterceptor
|
|
import com.hippo.ehviewer.coil.QrCodeInterceptor
|
|
import com.hippo.ehviewer.dailycheck.checkDawn
|
|
import com.hippo.ehviewer.download.DownloadManager
|
|
import com.hippo.ehviewer.download.DownloadsFilterMode
|
|
import com.hippo.ehviewer.ktbuilder.diskCache
|
|
import com.hippo.ehviewer.ktbuilder.imageLoader
|
|
import com.hippo.ehviewer.ktor.Cronet
|
|
import com.hippo.ehviewer.ktor.configureClient
|
|
import com.hippo.ehviewer.ktor.configureCommon
|
|
import com.hippo.ehviewer.ktor.isCronetAvailable
|
|
import com.hippo.ehviewer.ui.keepNoMediaFileStatus
|
|
import com.hippo.ehviewer.ui.screen.detailCache
|
|
import com.hippo.ehviewer.ui.tools.dataStateFlow
|
|
import com.hippo.ehviewer.util.AppConfig
|
|
import com.hippo.ehviewer.util.CrashHandler
|
|
import com.hippo.ehviewer.util.FavouriteStatusRouter
|
|
import com.hippo.ehviewer.util.FileUtils
|
|
import com.hippo.ehviewer.util.OSUtils
|
|
import io.ktor.client.HttpClient
|
|
import io.ktor.client.engine.okhttp.OkHttp
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.launch
|
|
import logcat.AndroidLogcatLogger
|
|
import logcat.LogPriority
|
|
import logcat.LogcatLogger
|
|
import logcat.asLog
|
|
import okio.Path.Companion.toOkioPath
|
|
import splitties.init.appCtx
|
|
|
|
private val lifecycle = ProcessLifecycleOwner.get().lifecycle
|
|
private val lifecycleScope = lifecycle.coroutineScope
|
|
|
|
class EhApplication : Application(), SingletonImageLoader.Factory {
|
|
override fun onCreate() = with(lifecycleScope) {
|
|
initSETConnection()
|
|
// Initialize Settings on first access
|
|
launchIO {
|
|
val mode = Settings.theme.value
|
|
if (!isAtLeastS) {
|
|
withUIContext {
|
|
AppCompatDelegate.setDefaultNightMode(mode)
|
|
}
|
|
}
|
|
LogcatLogger.loggers += AndroidLogcatLogger(LogPriority.VERBOSE)
|
|
Settings.saveCrashLog.valueFlow().collect {
|
|
if (it) {
|
|
LogcatLogger.install()
|
|
} else {
|
|
LogcatLogger.uninstall()
|
|
}
|
|
}
|
|
}
|
|
CrashHandler.install()
|
|
super.onCreate()
|
|
System.loadLibrary("ehviewer")
|
|
launch {
|
|
FavouriteStatusRouter.collect { info ->
|
|
detailCache[info.gid]?.apply {
|
|
favoriteSlot = info.favoriteSlot
|
|
favoriteName = info.favoriteName
|
|
favoriteNote = info.favoriteNote
|
|
}
|
|
}
|
|
}
|
|
launchIO {
|
|
EhTagDatabase.launchUpdate()
|
|
@Suppress("UNUSED_EXPRESSION")
|
|
launch { EhDB }
|
|
launch { dataStateFlow.value }
|
|
launch { OSUtils.totalMemory }
|
|
launch {
|
|
if (DownloadManager.labelList.isNotEmpty() && Settings.downloadFilterMode !in Settings.snapshot()) {
|
|
Settings.downloadFilterMode.value = DownloadsFilterMode.CUSTOM.flag
|
|
}
|
|
initialized = true
|
|
DownloadManager.readMetadataFromLocal()
|
|
}
|
|
launch {
|
|
FileUtils.cleanupDirectory(AppConfig.externalCrashDir)
|
|
FileUtils.cleanupDirectory(AppConfig.externalParseErrorDir)
|
|
}
|
|
launch { cleanupDownload() }
|
|
if (Settings.requestNews.value) {
|
|
launch { checkDawn() }
|
|
}
|
|
}
|
|
if (BuildConfig.DEBUG) {
|
|
StrictMode.enableDefaults()
|
|
Composer.setDiagnosticStackTraceMode(ComposeStackTraceMode.SourceInformation)
|
|
} else {
|
|
Composer.setDiagnosticStackTraceMode(ComposeStackTraceMode.Auto)
|
|
}
|
|
}
|
|
|
|
private suspend fun cleanupDownload() {
|
|
runCatching {
|
|
keepNoMediaFileStatus()
|
|
}.onFailure {
|
|
logcat(it)
|
|
}
|
|
runCatching {
|
|
clearTempDir()
|
|
}.onFailure {
|
|
logcat(it)
|
|
}
|
|
}
|
|
|
|
private fun clearTempDir() {
|
|
AppConfig.tempDir.deleteContent()
|
|
AppConfig.externalTempDir?.deleteContent()
|
|
}
|
|
|
|
override fun newImageLoader(context: Context) = context.imageLoader {
|
|
interceptorCoroutineContext(Dispatchers.Default)
|
|
components {
|
|
serviceLoaderEnabled(false)
|
|
add(
|
|
KtorNetworkFetcherFactory(
|
|
httpClient = { ktorClient },
|
|
connectivityChecker = { ConnectivityChecker.ONLINE },
|
|
),
|
|
)
|
|
add(MergeInterceptor)
|
|
add(DownloadThumbInterceptor)
|
|
if (isAtLeastO) {
|
|
add(HardwareBitmapInterceptor)
|
|
} else {
|
|
allowRgb565(true)
|
|
}
|
|
add(CropBorderInterceptor)
|
|
add(DetectBorderInterceptor)
|
|
add(QrCodeInterceptor)
|
|
add(MapExtraInfoInterceptor)
|
|
add(AnimatedWebPDecoder.Factory)
|
|
if (isAtLeastP) {
|
|
add(AnimatedImageDecoder.Factory(false))
|
|
} else {
|
|
add(GifDecoder.Factory())
|
|
}
|
|
}
|
|
diskCache { thumbCache }
|
|
crossfade(300)
|
|
val drawable = AppCompatResources.getDrawable(appCtx, R.drawable.image_failed)
|
|
if (drawable != null) error(drawable.asImage(true))
|
|
if (BuildConfig.DEBUG) {
|
|
logger(DebugLogger())
|
|
} else {
|
|
eventListener(object : EventListener() {
|
|
override fun onError(request: ImageRequest, result: ErrorResult) {
|
|
logcat("ImageLoader", LogPriority.ERROR) {
|
|
"🚨 Failed - ${request.data}\n${result.throwable.asLog()}"
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
@Volatile
|
|
var initialized = false
|
|
private set
|
|
|
|
val ktorClient by lazy {
|
|
if (isCronetAvailable && Settings.enableCronet.value) {
|
|
HttpClient(Cronet) {
|
|
engine { configureClient(Settings.enableQuic.value) }
|
|
configureCommon()
|
|
}
|
|
} else {
|
|
HttpClient(OkHttp) {
|
|
engine { configureClient() }
|
|
configureCommon()
|
|
}
|
|
}
|
|
}
|
|
|
|
val noRedirectKtorClient by lazy {
|
|
HttpClient(ktorClient.engine) {
|
|
configureCommon(redirect = false)
|
|
}
|
|
}
|
|
|
|
val thumbCache by lazy {
|
|
diskCache {
|
|
directory(appCtx.cacheDir.toOkioPath() / "thumb")
|
|
maxSizeBytes(80L * 1024 * 1024)
|
|
}
|
|
}
|
|
|
|
val searchDatabase by lazy { roomDb<SearchDatabase>("search_database.db") }
|
|
}
|
|
}
|