Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bbaa74d99c | |||
| 310b1ad69b | |||
| 7f37989c4e | |||
| 185920b984 | |||
| 4639077756 | |||
| 0bf1519c25 | |||
| 45a36cef32 | |||
| dece1bc0cb | |||
| eaffd3f2dc | |||
| aabe409ee5 | |||
| e626cdd030 | |||
| b161c333ec | |||
| e587bb7f44 |
@@ -1,9 +0,0 @@
|
|||||||
package mihon.core.firebase
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import eu.kanade.tachiyomi.core.security.PrivacyPreferences
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
|
|
||||||
object Firebase {
|
|
||||||
fun setup(context: Context, preference: PrivacyPreferences, scope: CoroutineScope) = Unit
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package mihon.core.firebase
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
object FirebaseConfig {
|
||||||
|
fun init(context: Context) = Unit
|
||||||
|
|
||||||
|
fun setAnalyticsEnabled(enabled: Boolean) = Unit
|
||||||
|
|
||||||
|
fun setCrashlyticsEnabled(enabled: Boolean) = Unit
|
||||||
|
}
|
||||||
+1
-1
@@ -25,7 +25,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import nl.adaptivity.xmlutil.AndroidXmlReader
|
import nl.adaptivity.xmlutil.core.AndroidXmlReader
|
||||||
import nl.adaptivity.xmlutil.serialization.XML
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
import nl.adaptivity.xmlutil.serialization.XmlValue
|
import nl.adaptivity.xmlutil.serialization.XmlValue
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import logcat.LogcatLogger
|
import logcat.LogcatLogger
|
||||||
import mihon.core.firebase.Firebase
|
import mihon.core.firebase.FirebaseConfig
|
||||||
import mihon.core.migration.Migrator
|
import mihon.core.migration.Migrator
|
||||||
import mihon.core.migration.migrations.migrations
|
import mihon.core.migration.migrations.migrations
|
||||||
import org.conscrypt.Conscrypt
|
import org.conscrypt.Conscrypt
|
||||||
@@ -100,6 +100,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
@SuppressLint("LaunchActivityFromNotification")
|
@SuppressLint("LaunchActivityFromNotification")
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super<Application>.onCreate()
|
super<Application>.onCreate()
|
||||||
|
FirebaseConfig.init(applicationContext)
|
||||||
|
|
||||||
GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java)
|
GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java)
|
||||||
|
|
||||||
@@ -127,12 +128,12 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
setupExhLogging() // EXH logging
|
setupExhLogging() // EXH logging
|
||||||
LogcatLogger.install(XLogLogcatLogger()) // SY Redirect Logcat to XLog
|
LogcatLogger.install(XLogLogcatLogger()) // SY Redirect Logcat to XLog
|
||||||
|
|
||||||
Firebase.setup(applicationContext, privacyPreferences, ProcessLifecycleOwner.get().lifecycleScope)
|
|
||||||
|
|
||||||
setupNotificationChannels()
|
setupNotificationChannels()
|
||||||
|
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||||
|
|
||||||
|
val scope = ProcessLifecycleOwner.get().lifecycleScope
|
||||||
|
|
||||||
// Show notification to disable Incognito Mode when it's enabled
|
// Show notification to disable Incognito Mode when it's enabled
|
||||||
basePreferences.incognitoMode().changes()
|
basePreferences.incognitoMode().changes()
|
||||||
.onEach { enabled ->
|
.onEach { enabled ->
|
||||||
@@ -160,14 +161,22 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
cancelNotification(Notifications.ID_INCOGNITO_MODE)
|
cancelNotification(Notifications.ID_INCOGNITO_MODE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
|
.launchIn(scope)
|
||||||
|
|
||||||
|
privacyPreferences.analytics()
|
||||||
|
.changes()
|
||||||
|
.onEach(FirebaseConfig::setAnalyticsEnabled)
|
||||||
|
.launchIn(scope)
|
||||||
|
|
||||||
|
privacyPreferences.crashlytics()
|
||||||
|
.changes()
|
||||||
|
.onEach(FirebaseConfig::setCrashlyticsEnabled)
|
||||||
|
.launchIn(scope)
|
||||||
|
|
||||||
setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode().get())
|
setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode().get())
|
||||||
|
|
||||||
// Updates widget update
|
// Updates widget update
|
||||||
with(WidgetManager(Injekt.get(), Injekt.get())) {
|
WidgetManager(Injekt.get(), Injekt.get()).apply { init(scope) }
|
||||||
init(ProcessLifecycleOwner.get().lifecycleScope)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if (!LogcatLogger.isInstalled && networkPreferences.verboseLogging().get()) {
|
/*if (!LogcatLogger.isInstalled && networkPreferences.verboseLogging().get()) {
|
||||||
LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
|
LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package eu.kanade.tachiyomi.crash
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.google.firebase.crashlytics.ktx.crashlytics
|
|
||||||
import com.google.firebase.ktx.Firebase
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
@@ -13,7 +11,6 @@ import kotlinx.serialization.encoding.Encoder
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
class GlobalExceptionHandler private constructor(
|
class GlobalExceptionHandler private constructor(
|
||||||
private val applicationContext: Context,
|
private val applicationContext: Context,
|
||||||
@@ -33,14 +30,9 @@ class GlobalExceptionHandler private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun uncaughtException(thread: Thread, exception: Throwable) {
|
override fun uncaughtException(thread: Thread, exception: Throwable) {
|
||||||
try {
|
logcat(priority = LogPriority.ERROR, throwable = exception)
|
||||||
logcat(priority = LogPriority.ERROR, throwable = exception)
|
launchActivity(applicationContext, activityToBeLaunched, exception)
|
||||||
Firebase.crashlytics.recordException(exception)
|
defaultHandler.uncaughtException(thread, exception)
|
||||||
launchActivity(applicationContext, activityToBeLaunched, exception)
|
|
||||||
exitProcess(0)
|
|
||||||
} catch (_: Exception) {
|
|
||||||
defaultHandler.uncaughtException(thread, exception)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchActivity(
|
private fun launchActivity(
|
||||||
|
|||||||
@@ -96,13 +96,13 @@ class DownloadCache(
|
|||||||
private val diskCacheFile: File
|
private val diskCacheFile: File
|
||||||
get() = File(context.cacheDir, "dl_index_cache_v3")
|
get() = File(context.cacheDir, "dl_index_cache_v3")
|
||||||
|
|
||||||
private val rootDownloadsDirLock = Mutex()
|
private val rootDownloadsDirMutex = Mutex()
|
||||||
private var rootDownloadsDir = RootDirectory(storageManager.getDownloadsDirectory())
|
private var rootDownloadsDir = RootDirectory(storageManager.getDownloadsDirectory())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Attempt to read cache file
|
// Attempt to read cache file
|
||||||
scope.launch {
|
scope.launch {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
try {
|
try {
|
||||||
if (diskCacheFile.exists()) {
|
if (diskCacheFile.exists()) {
|
||||||
val diskCache = diskCacheFile.inputStream().use {
|
val diskCache = diskCacheFile.inputStream().use {
|
||||||
@@ -112,7 +112,7 @@ class DownloadCache(
|
|||||||
lastRenew = System.currentTimeMillis()
|
lastRenew = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
logcat(LogPriority.ERROR, e) { "Failed to initialize disk cache" }
|
logcat(LogPriority.ERROR, e) { "Failed to initialize from disk cache" }
|
||||||
diskCacheFile.delete()
|
diskCacheFile.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +200,7 @@ class DownloadCache(
|
|||||||
* @param manga the manga of the chapter.
|
* @param manga the manga of the chapter.
|
||||||
*/
|
*/
|
||||||
suspend fun addChapter(chapterDirName: String, mangaUniFile: UniFile, manga: Manga) {
|
suspend fun addChapter(chapterDirName: String, mangaUniFile: UniFile, manga: Manga) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
// Retrieve the cached source directory or cache a new one
|
// Retrieve the cached source directory or cache a new one
|
||||||
var sourceDir = rootDownloadsDir.sourceDirs[manga.source]
|
var sourceDir = rootDownloadsDir.sourceDirs[manga.source]
|
||||||
if (sourceDir == null) {
|
if (sourceDir == null) {
|
||||||
@@ -232,7 +232,7 @@ class DownloadCache(
|
|||||||
* @param manga the manga of the chapter.
|
* @param manga the manga of the chapter.
|
||||||
*/
|
*/
|
||||||
suspend fun removeChapter(chapter: Chapter, manga: Manga) {
|
suspend fun removeChapter(chapter: Chapter, manga: Manga) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||||
val mangaDir = sourceDir.mangaDirs[
|
val mangaDir = sourceDir.mangaDirs[
|
||||||
provider.getMangaDirName(
|
provider.getMangaDirName(
|
||||||
@@ -251,7 +251,7 @@ class DownloadCache(
|
|||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
suspend fun removeFolders(folders: List<String>, manga: Manga) {
|
suspend fun removeFolders(folders: List<String>, manga: Manga) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.ogTitle)] ?: return
|
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(manga.ogTitle)] ?: return
|
||||||
folders.forEach { chapter ->
|
folders.forEach { chapter ->
|
||||||
@@ -271,7 +271,7 @@ class DownloadCache(
|
|||||||
* @param manga the manga of the chapter.
|
* @param manga the manga of the chapter.
|
||||||
*/
|
*/
|
||||||
suspend fun removeChapters(chapters: List<Chapter>, manga: Manga) {
|
suspend fun removeChapters(chapters: List<Chapter>, manga: Manga) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||||
val mangaDir = sourceDir.mangaDirs[
|
val mangaDir = sourceDir.mangaDirs[
|
||||||
provider.getMangaDirName(
|
provider.getMangaDirName(
|
||||||
@@ -296,7 +296,7 @@ class DownloadCache(
|
|||||||
* @param manga the manga to remove.
|
* @param manga the manga to remove.
|
||||||
*/
|
*/
|
||||||
suspend fun removeManga(manga: Manga) {
|
suspend fun removeManga(manga: Manga) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
val sourceDir = rootDownloadsDir.sourceDirs[manga.source] ?: return
|
||||||
val mangaDirName = provider.getMangaDirName(/* SY --> */ manga.ogTitle /* SY <-- */)
|
val mangaDirName = provider.getMangaDirName(/* SY --> */ manga.ogTitle /* SY <-- */)
|
||||||
if (sourceDir.mangaDirs.containsKey(mangaDirName)) {
|
if (sourceDir.mangaDirs.containsKey(mangaDirName)) {
|
||||||
@@ -308,7 +308,7 @@ class DownloadCache(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun removeSource(source: Source) {
|
suspend fun removeSource(source: Source) {
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
rootDownloadsDir.sourceDirs -= source.id
|
rootDownloadsDir.sourceDirs -= source.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,10 +349,10 @@ class DownloadCache(
|
|||||||
|
|
||||||
val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id }
|
val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id }
|
||||||
|
|
||||||
rootDownloadsDirLock.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
rootDownloadsDir = RootDirectory(storageManager.getDownloadsDirectory())
|
val updatedRootDir = RootDirectory(storageManager.getDownloadsDirectory())
|
||||||
|
|
||||||
val sourceDirs = rootDownloadsDir.dir?.listFiles().orEmpty()
|
updatedRootDir.sourceDirs = updatedRootDir.dir?.listFiles().orEmpty()
|
||||||
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
||||||
.mapNotNull { dir ->
|
.mapNotNull { dir ->
|
||||||
val sourceId = sourceMap[dir.name!!.lowercase()]
|
val sourceId = sourceMap[dir.name!!.lowercase()]
|
||||||
@@ -360,36 +360,35 @@ class DownloadCache(
|
|||||||
}
|
}
|
||||||
.toMap()
|
.toMap()
|
||||||
|
|
||||||
rootDownloadsDir.sourceDirs = sourceDirs
|
updatedRootDir.sourceDirs.values.map { sourceDir ->
|
||||||
|
async {
|
||||||
|
sourceDir.mangaDirs = sourceDir.dir?.listFiles().orEmpty()
|
||||||
|
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
||||||
|
.associate { it.name!! to MangaDirectory(it) }
|
||||||
|
|
||||||
sourceDirs.values
|
sourceDir.mangaDirs.values.forEach { mangaDir ->
|
||||||
.map { sourceDir ->
|
val chapterDirs = mangaDir.dir?.listFiles().orEmpty()
|
||||||
async {
|
.mapNotNull {
|
||||||
sourceDir.mangaDirs = sourceDir.dir?.listFiles().orEmpty()
|
when {
|
||||||
.filter { it.isDirectory && !it.name.isNullOrBlank() }
|
// Ignore incomplete downloads
|
||||||
.associate { it.name!! to MangaDirectory(it) }
|
it.name?.endsWith(Downloader.TMP_DIR_SUFFIX) == true -> null
|
||||||
|
// Folder of images
|
||||||
sourceDir.mangaDirs.values.forEach { mangaDir ->
|
it.isDirectory -> it.name
|
||||||
val chapterDirs = mangaDir.dir?.listFiles().orEmpty()
|
// CBZ files
|
||||||
.mapNotNull {
|
it.isFile && it.extension == "cbz" -> it.nameWithoutExtension
|
||||||
when {
|
// Anything else is irrelevant
|
||||||
// Ignore incomplete downloads
|
else -> null
|
||||||
it.name?.endsWith(Downloader.TMP_DIR_SUFFIX) == true -> null
|
|
||||||
// Folder of images
|
|
||||||
it.isDirectory -> it.name
|
|
||||||
// CBZ files
|
|
||||||
it.isFile && it.extension == "cbz" -> it.nameWithoutExtension
|
|
||||||
// Anything else is irrelevant
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.toMutableSet()
|
}
|
||||||
|
.toMutableSet()
|
||||||
|
|
||||||
mangaDir.chapterDirs = chapterDirs
|
mangaDir.chapterDirs = chapterDirs
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.awaitAll()
|
.awaitAll()
|
||||||
|
|
||||||
|
rootDownloadsDir = updatedRootDir
|
||||||
}
|
}
|
||||||
|
|
||||||
_isInitializing.emit(false)
|
_isInitializing.emit(false)
|
||||||
|
|||||||
@@ -346,12 +346,13 @@ class MainActivity : BaseActivity() {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun HandleOnNewIntent(context: Context, navigator: Navigator) {
|
private fun HandleOnNewIntent(context: Context, navigator: Navigator) {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
callbackFlow<Intent> {
|
callbackFlow {
|
||||||
val componentActivity = context as ComponentActivity
|
val componentActivity = context as ComponentActivity
|
||||||
val consumer = Consumer<Intent> { trySend(it) }
|
val consumer = Consumer<Intent> { trySend(it) }
|
||||||
componentActivity.addOnNewIntentListener(consumer)
|
componentActivity.addOnNewIntentListener(consumer)
|
||||||
awaitClose { componentActivity.removeOnNewIntentListener(consumer) }
|
awaitClose { componentActivity.removeOnNewIntentListener(consumer) }
|
||||||
}.collectLatest { handleIntentAction(it, navigator) }
|
}
|
||||||
|
.collectLatest { handleIntentAction(it, navigator) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,6 +408,7 @@ class MainActivity : BaseActivity() {
|
|||||||
* When custom animation is used, status and navigation bar color will be set to transparent and will be restored
|
* When custom animation is used, status and navigation bar color will be set to transparent and will be restored
|
||||||
* after the animation is finished.
|
* after the animation is finished.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("Deprecation")
|
||||||
private fun setSplashScreenExitAnimation(splashScreen: SplashScreen?) {
|
private fun setSplashScreenExitAnimation(splashScreen: SplashScreen?) {
|
||||||
val root = findViewById<View>(android.R.id.content)
|
val root = findViewById<View>(android.R.id.content)
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && splashScreen != null) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && splashScreen != null) {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package mihon.core.firebase
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.google.firebase.analytics.FirebaseAnalytics
|
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import eu.kanade.tachiyomi.core.security.PrivacyPreferences
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
|
|
||||||
object Firebase {
|
|
||||||
fun setup(context: Context, preference: PrivacyPreferences, scope: CoroutineScope) {
|
|
||||||
preference.analytics().changes().onEach { enabled ->
|
|
||||||
FirebaseAnalytics.getInstance(context).setAnalyticsCollectionEnabled(enabled)
|
|
||||||
}.launchIn(scope)
|
|
||||||
preference.crashlytics().changes().onEach { enabled ->
|
|
||||||
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(enabled)
|
|
||||||
}.launchIn(scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package mihon.core.firebase
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.google.firebase.FirebaseApp
|
||||||
|
import com.google.firebase.analytics.FirebaseAnalytics
|
||||||
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
|
|
||||||
|
object FirebaseConfig {
|
||||||
|
private lateinit var analytics: FirebaseAnalytics
|
||||||
|
private lateinit var crashlytics: FirebaseCrashlytics
|
||||||
|
|
||||||
|
fun init(context: Context) {
|
||||||
|
analytics = FirebaseAnalytics.getInstance(context)
|
||||||
|
FirebaseApp.initializeApp(context)
|
||||||
|
crashlytics = FirebaseCrashlytics.getInstance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAnalyticsEnabled(enabled: Boolean) {
|
||||||
|
analytics.setAnalyticsCollectionEnabled(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCrashlyticsEnabled(enabled: Boolean) {
|
||||||
|
crashlytics.isCrashlyticsCollectionEnabled = enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ interpolator_version = "1.0.0"
|
|||||||
[libraries]
|
[libraries]
|
||||||
gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" }
|
gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" }
|
||||||
|
|
||||||
annotation = "androidx.annotation:annotation:1.8.2"
|
annotation = "androidx.annotation:annotation:1.9.0"
|
||||||
appcompat = "androidx.appcompat:appcompat:1.7.0"
|
appcompat = "androidx.appcompat:appcompat:1.7.0"
|
||||||
biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
||||||
constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4"
|
constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||||
@@ -28,7 +28,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag
|
|||||||
|
|
||||||
interpolator = { group = "androidx.interpolator", name = "interpolator", version.ref = "interpolator_version" }
|
interpolator = { group = "androidx.interpolator", name = "interpolator", version.ref = "interpolator_version" }
|
||||||
|
|
||||||
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.3.2"
|
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.3.3"
|
||||||
test-ext = "androidx.test.ext:junit-ktx:1.2.1"
|
test-ext = "androidx.test.ext:junit-ktx:1.2.1"
|
||||||
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.1"
|
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.1"
|
||||||
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0"
|
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[versions]
|
[versions]
|
||||||
compose-bom = "2024.09.03"
|
compose-bom = "2024.10.00"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
activity = "androidx.activity:activity-compose:1.9.2"
|
activity = "androidx.activity:activity-compose:1.9.3"
|
||||||
bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||||
foundation = { module = "androidx.compose.foundation:foundation" }
|
foundation = { module = "androidx.compose.foundation:foundation" }
|
||||||
animation = { module = "androidx.compose.animation:animation" }
|
animation = { module = "androidx.compose.animation:animation" }
|
||||||
@@ -15,4 +15,4 @@ ui-util = { module = "androidx.compose.ui:ui-util" }
|
|||||||
material3-core = { module = "androidx.compose.material3:material3" }
|
material3-core = { module = "androidx.compose.material3:material3" }
|
||||||
material-icons = { module = "androidx.compose.material:material-icons-extended" }
|
material-icons = { module = "androidx.compose.material:material-icons-extended" }
|
||||||
|
|
||||||
glance = "androidx.glance:glance-appwidget:1.1.0"
|
glance = "androidx.glance:glance-appwidget:1.1.1"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
kotlin_version = "2.0.21"
|
kotlin_version = "2.0.21"
|
||||||
serialization_version = "1.7.3"
|
serialization_version = "1.7.3"
|
||||||
xml_serialization_version = "0.86.3"
|
xml_serialization_version = "0.90.2"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin_version" }
|
reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin_version" }
|
||||||
|
|||||||
+1
-1
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This module houses the string resources and translations.
|
This module houses the string resources and translations.
|
||||||
|
|
||||||
Original English strings are manged in `src/commonMain/resources/MR/base/`. Translations are done externally via Weblate. See [our website](https://mihon.app/docs/contribute#translation) for more details.
|
Original English strings are managed in `src/commonMain/moko-resources/base/`. Translations are done externally via Weblate. See [our website](https://mihon.app/docs/contribute#translation) for more details.
|
||||||
@@ -582,7 +582,7 @@
|
|||||||
<string name="pref_reset_user_agent_string">Reset default user agent string</string>
|
<string name="pref_reset_user_agent_string">Reset default user agent string</string>
|
||||||
<string name="requires_app_restart">Requires app restart to take effect</string>
|
<string name="requires_app_restart">Requires app restart to take effect</string>
|
||||||
<string name="cookies_cleared">Cookies cleared</string>
|
<string name="cookies_cleared">Cookies cleared</string>
|
||||||
<string name="pref_invalidate_download_cache">Invalidate downloads index</string>
|
<string name="pref_invalidate_download_cache">Reindex downloads</string>
|
||||||
<string name="pref_invalidate_download_cache_summary">Force app to recheck downloaded chapters</string>
|
<string name="pref_invalidate_download_cache_summary">Force app to recheck downloaded chapters</string>
|
||||||
<string name="download_cache_invalidated">Downloads index invalidated</string>
|
<string name="download_cache_invalidated">Downloads index invalidated</string>
|
||||||
<string name="pref_clear_database">Clear database</string>
|
<string name="pref_clear_database">Clear database</string>
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import tachiyomi.core.common.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> Preference<T>.collectAsState(scope: CoroutineScope = rememberCoroutineScope()): State<T> {
|
fun <T> Preference<T>.collectAsState(): State<T> {
|
||||||
val flow = remember(this) { stateIn(scope) }
|
val flow = remember(this) { changes() }
|
||||||
return flow.collectAsState()
|
return flow.collectAsState(initial = get())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import kotlinx.serialization.json.decodeFromStream
|
|||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import mihon.core.common.archive.ZipWriter
|
import mihon.core.common.archive.ZipWriter
|
||||||
import mihon.core.common.archive.archiveReader
|
import mihon.core.common.archive.archiveReader
|
||||||
import nl.adaptivity.xmlutil.AndroidXmlReader
|
import nl.adaptivity.xmlutil.core.AndroidXmlReader
|
||||||
import nl.adaptivity.xmlutil.serialization.XML
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.core.common.storage.extension
|
import tachiyomi.core.common.storage.extension
|
||||||
|
|||||||
Reference in New Issue
Block a user