diff --git a/api/api/api.api b/api/api/api.api index ed5296c65..01f18cf8c 100644 --- a/api/api/api.api +++ b/api/api/api.api @@ -1,12 +1,7 @@ public abstract interface class app/revanced/manager/downloader/BaseDownloadScope : app/revanced/manager/downloader/Scope { } -public final class app/revanced/manager/downloader/ConstantsKt { - public static final field DOWNLOADER_HOST_PERMISSION Ljava/lang/String; -} - public final class app/revanced/manager/downloader/DownloadUrl : android/os/Parcelable { - public static final field $stable I public static final field CREATOR Landroid/os/Parcelable$Creator; public fun (Ljava/lang/String;Ljava/util/Map;)V public synthetic fun (Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -25,11 +20,9 @@ public final class app/revanced/manager/downloader/DownloadUrl : android/os/Parc } public final class app/revanced/manager/downloader/Downloader { - public static final field $stable I } public final class app/revanced/manager/downloader/DownloaderBuilder { - public static final field $stable I } public abstract interface annotation class app/revanced/manager/downloader/DownloaderHostApi : java/lang/annotation/Annotation { @@ -40,9 +33,9 @@ public final class app/revanced/manager/downloader/DownloaderKt { } public final class app/revanced/manager/downloader/DownloaderScope : app/revanced/manager/downloader/Scope { - public static final field $stable I public final fun download (Lkotlin/jvm/functions/Function3;)V public final fun get (Lkotlin/jvm/functions/Function4;)V + public fun getDataDir ()Ljava/io/File; public fun getDownloaderPackageName ()Ljava/lang/String; public fun getHostPackageName ()Ljava/lang/String; public final fun useService (Landroid/content/Intent;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -54,6 +47,11 @@ public final class app/revanced/manager/downloader/ExtensionsKt { public abstract interface class app/revanced/manager/downloader/GetScope : app/revanced/manager/downloader/Scope { public abstract fun requestStartActivity (Landroid/content/Intent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun requestStartFragment (Ljava/lang/Class;Landroid/os/Bundle;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class app/revanced/manager/downloader/GetScope$DefaultImpls { + public static fun requestStartFragment (Lapp/revanced/manager/downloader/GetScope;Ljava/lang/Class;Landroid/os/Bundle;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public abstract interface class app/revanced/manager/downloader/InputDownloadScope : app/revanced/manager/downloader/BaseDownloadScope { @@ -64,7 +62,6 @@ public abstract interface class app/revanced/manager/downloader/OutputDownloadSc } public final class app/revanced/manager/downloader/Package : android/os/Parcelable { - public static final field $stable I public static final field CREATOR Landroid/os/Parcelable$Creator; public fun (Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; @@ -81,32 +78,28 @@ public final class app/revanced/manager/downloader/Package : android/os/Parcelab } public abstract interface class app/revanced/manager/downloader/Scope { + public abstract fun getDataDir ()Ljava/io/File; public abstract fun getDownloaderPackageName ()Ljava/lang/String; public abstract fun getHostPackageName ()Ljava/lang/String; } public abstract class app/revanced/manager/downloader/UserInteractionException : java/lang/Exception { - public static final field $stable I public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V } public abstract class app/revanced/manager/downloader/UserInteractionException$Activity : app/revanced/manager/downloader/UserInteractionException { - public static final field $stable I public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V } public final class app/revanced/manager/downloader/UserInteractionException$Activity$Cancelled : app/revanced/manager/downloader/UserInteractionException$Activity { - public static final field $stable I } public final class app/revanced/manager/downloader/UserInteractionException$Activity$NotCompleted : app/revanced/manager/downloader/UserInteractionException$Activity { - public static final field $stable I public final fun getIntent ()Landroid/content/Intent; public final fun getResultCode ()I } public final class app/revanced/manager/downloader/UserInteractionException$RequestDenied : app/revanced/manager/downloader/UserInteractionException { - public static final field $stable I } public final class app/revanced/manager/downloader/webview/APIKt { @@ -149,8 +142,8 @@ public abstract interface class app/revanced/manager/downloader/webview/WebViewC } public final class app/revanced/manager/downloader/webview/WebViewScope : app/revanced/manager/downloader/Scope { - public static final field $stable I public final fun download (Lkotlin/jvm/functions/Function5;)V + public fun getDataDir ()Ljava/io/File; public fun getDownloaderPackageName ()Ljava/lang/String; public fun getHostPackageName ()Ljava/lang/String; public final fun pageLoad (Lkotlin/jvm/functions/Function3;)V diff --git a/api/src/main/kotlin/app/revanced/manager/downloader/Constants.kt b/api/src/main/kotlin/app/revanced/manager/downloader/Constants.kt deleted file mode 100644 index 4f96167e6..000000000 --- a/api/src/main/kotlin/app/revanced/manager/downloader/Constants.kt +++ /dev/null @@ -1,7 +0,0 @@ -package app.revanced.manager.downloader - -/** - * The permission ID of the special downloader host permission. Only ReVanced Manager will have this permission. - * Downloader UI activities and internal services can be protected using this permission. - */ -const val DOWNLOADER_HOST_PERMISSION = "app.revanced.manager.permission.DOWNLOADER_HOST" \ No newline at end of file diff --git a/api/src/main/kotlin/app/revanced/manager/downloader/Downloader.kt b/api/src/main/kotlin/app/revanced/manager/downloader/Downloader.kt index 7e92992ac..5ff3efb8d 100644 --- a/api/src/main/kotlin/app/revanced/manager/downloader/Downloader.kt +++ b/api/src/main/kotlin/app/revanced/manager/downloader/Downloader.kt @@ -6,6 +6,7 @@ import android.content.Intent import android.content.ServiceConnection import android.os.IBinder import android.app.Activity +import android.content.res.Resources import android.os.Bundle import android.os.Parcelable import androidx.annotation.StringRes @@ -94,7 +95,8 @@ typealias GetResult = Pair class DownloaderScope internal constructor( private val scopeImpl: Scope, - internal val context: Context + internal val context: Context, + internal val resources: Resources ) : Scope by scopeImpl { // Returning an InputStream is the primary way for a downloader to implement the download function, but we also want to offer an OutputStream API since using InputStream might not be convenient in all cases. // It is much easier to implement the main InputStream API on top of OutputStreams compared to doing it the other way around, which is why we are using OutputStream here. @@ -156,8 +158,8 @@ class DownloaderBuilder internal constructor( private val block: DownloaderScope.() -> Unit ) { @DownloaderHostApi - fun build(scopeImpl: Scope, context: Context) = - with(DownloaderScope(scopeImpl, context)) { + fun build(scopeImpl: Scope, context: Context, resources: Resources) = + with(DownloaderScope(scopeImpl, context, resources)) { block() Downloader( diff --git a/api/src/main/kotlin/app/revanced/manager/downloader/webview/API.kt b/api/src/main/kotlin/app/revanced/manager/downloader/webview/API.kt index 65cb4fb79..79abfc2d2 100644 --- a/api/src/main/kotlin/app/revanced/manager/downloader/webview/API.kt +++ b/api/src/main/kotlin/app/revanced/manager/downloader/webview/API.kt @@ -144,7 +144,7 @@ suspend fun GetScope.runWebView( */ fun WebViewDownloader(@StringRes name: Int, block: suspend WebViewScope.(packageName: String, version: String?) -> InitialUrl?) = Downloader(name) { - val label = context.getString(name) + val label = resources.getString(name) get { packageName, version -> class ReturnNull : Exception() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5c24e86e7..f9ae36f71 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,14 +2,6 @@ - - - diff --git a/app/src/main/java/app/revanced/manager/DownloaderActivity.kt b/app/src/main/java/app/revanced/manager/DownloaderActivity.kt index 48336b734..9a2df9239 100644 --- a/app/src/main/java/app/revanced/manager/DownloaderActivity.kt +++ b/app/src/main/java/app/revanced/manager/DownloaderActivity.kt @@ -36,9 +36,8 @@ class DownloaderActivity : FragmentActivity() { val fragmentClassName = intent.getStringExtra("FRAGMENT_CLASS_NAME")!! val args = intent.getBundleExtra("FRAGMENT_ARGS") - res = - downloaderPkgState?.context?.createConfigurationContext(super.resources.configuration)?.resources - + // See DownloaderRepository.ResourceImpl. + res = downloaderPkgState?.resourceImpl?.apply(super.resources) if (savedInstanceState == null) { @Suppress("UNCHECKED_CAST") diff --git a/app/src/main/java/app/revanced/manager/ManagerApplication.kt b/app/src/main/java/app/revanced/manager/ManagerApplication.kt index 55348d79b..917c4a0f0 100644 --- a/app/src/main/java/app/revanced/manager/ManagerApplication.kt +++ b/app/src/main/java/app/revanced/manager/ManagerApplication.kt @@ -78,7 +78,7 @@ class ManagerApplication : Application() { arrayOf(patchBundleRepository, downloaderRepository).forEach { with(it) { reload() - updateCheck() + updateCheck(force = false) } } } diff --git a/app/src/main/java/app/revanced/manager/data/platform/NetworkInfo.kt b/app/src/main/java/app/revanced/manager/data/platform/NetworkInfo.kt index d8381bb8e..dcaa0dc88 100644 --- a/app/src/main/java/app/revanced/manager/data/platform/NetworkInfo.kt +++ b/app/src/main/java/app/revanced/manager/data/platform/NetworkInfo.kt @@ -9,11 +9,5 @@ class NetworkInfo(app: Application) { private val connectivityManager = app.getSystemService()!! private fun getCapabilities() = connectivityManager.activeNetwork?.let { connectivityManager.getNetworkCapabilities(it) } - fun isConnected() = connectivityManager.activeNetwork != null fun isUnmetered() = getCapabilities()?.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) ?: true - - /** - * Returns true if it is safe to download large files. - */ - fun isSafe(ignoreMetered: Boolean) = isConnected() && (ignoreMetered || isUnmetered()) } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/data/room/bundles/PatchBundleEntity.kt b/app/src/main/java/app/revanced/manager/data/room/bundles/PatchBundleEntity.kt index b82bf649e..68acc33e8 100644 --- a/app/src/main/java/app/revanced/manager/data/room/bundles/PatchBundleEntity.kt +++ b/app/src/main/java/app/revanced/manager/data/room/bundles/PatchBundleEntity.kt @@ -2,12 +2,13 @@ package app.revanced.manager.data.room.bundles import androidx.room.* import app.revanced.manager.data.room.sources.Source +import app.revanced.manager.domain.manager.SourceManager @Entity(tableName = "patch_bundles") data class PatchBundleEntity( - @PrimaryKey val uid: Int, + @PrimaryKey override val uid: Int, @ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "version") val versionHash: String? = null, @ColumnInfo(name = "source") val source: Source, @ColumnInfo(name = "auto_update") val autoUpdate: Boolean -) \ No newline at end of file +) : SourceManager.DatabaseEntity \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/data/room/downloader/DownloaderEntity.kt b/app/src/main/java/app/revanced/manager/data/room/downloader/DownloaderEntity.kt index 848980f87..4bbb8e697 100644 --- a/app/src/main/java/app/revanced/manager/data/room/downloader/DownloaderEntity.kt +++ b/app/src/main/java/app/revanced/manager/data/room/downloader/DownloaderEntity.kt @@ -2,12 +2,13 @@ package app.revanced.manager.data.room.downloader import androidx.room.* import app.revanced.manager.data.room.sources.Source +import app.revanced.manager.domain.manager.SourceManager @Entity(tableName = "downloaders") data class DownloaderEntity( - @PrimaryKey val uid: Int, + @PrimaryKey override val uid: Int, @ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "version") val versionHash: String? = null, @ColumnInfo(name = "source") val source: Source, @ColumnInfo(name = "auto_update") val autoUpdate: Boolean -) \ No newline at end of file +) : SourceManager.DatabaseEntity \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/domain/manager/SourceManager.kt b/app/src/main/java/app/revanced/manager/domain/manager/SourceManager.kt index bab4f9c0c..d3f0b33d4 100644 --- a/app/src/main/java/app/revanced/manager/domain/manager/SourceManager.kt +++ b/app/src/main/java/app/revanced/manager/domain/manager/SourceManager.kt @@ -11,23 +11,20 @@ import app.revanced.manager.data.room.AppDatabase.Companion.generateUid import app.revanced.manager.data.room.sources.Source as SourceInfo import app.revanced.manager.data.room.sources.SourceProperties import app.revanced.manager.domain.sources.APISource +import app.revanced.manager.domain.sources.Extensions.asRemoteOrNull import app.revanced.manager.domain.sources.LocalSource import app.revanced.manager.domain.sources.RemoteSource import app.revanced.manager.domain.sources.Source import app.revanced.manager.util.simpleMessage import app.revanced.manager.util.tag import app.revanced.manager.util.toast -import kotlinx.collections.immutable.PersistentMap -import kotlinx.collections.immutable.persistentMapOf -import kotlinx.collections.immutable.toPersistentMap import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -40,7 +37,7 @@ import kotlin.collections.set /** * Abstraction for managing a source system. Used by [app.revanced.manager.domain.repository.PatchBundleRepository] and [app.revanced.manager.domain.repository.DownloaderRepository]. */ -abstract class SourceManager( +abstract class SourceManager( initial: OUTPUT, protected val sourceDir: File ) : KoinComponent { @@ -48,8 +45,6 @@ abstract class SourceManager( protected val prefs: PreferencesManager by inject() protected val networkInfo: NetworkInfo by inject() - protected abstract val defaultSource: DB - protected abstract suspend fun dbGetAll(): List protected abstract suspend fun dbGetProps(uid: Int): SourceProperties? protected abstract suspend fun dbUpsert(entity: DB) @@ -59,28 +54,39 @@ abstract class SourceManager( protected abstract fun loadEntity(entity: DB): Source protected abstract fun entityFromProps(uid: Int, props: SourceProperties): DB - protected abstract fun uidOf(entity: DB): Int protected abstract fun realNameOf(loaded: LOADED): String? @get:StringRes protected abstract val updateUnavailable: Int + @get:StringRes protected abstract val updateSuccess: Int + @get:StringRes protected abstract val updateFailed: Int + @get:StringRes protected abstract val replaceFail: Int protected abstract suspend fun loadDataFromSources(sources: MutableMap>): OUTPUT - private val _updateError = MutableStateFlow(null) - val updateError = _updateError.asStateFlow() + protected val defaultSource = entityFromProps( + 0, SourceProperties( + name = "", + versionHash = null, + source = SourceInfo.API, + autoUpdate = false + ) + ) - private val _apiOutageError = MutableStateFlow(null) - val apiOutageError = _apiOutageError.asStateFlow() + protected val store = Store( + CoroutineScope(Dispatchers.Default), + State(data = initial) + ) - protected val store = - Store(CoroutineScope(Dispatchers.Default), State(data = initial)) + val updateErrors = store.state.map { it.updateErrors } + val apiOutageError = updateErrors.map { it[0] } + val hasOutdated = store.state.map { it.outdatedSources.isNotEmpty() } protected suspend inline fun dispatchAction( name: String, @@ -97,13 +103,13 @@ abstract class SourceManager( /** * Performs a reload. Do not call this outside of a store action. */ - protected suspend fun doReload(): State { + protected suspend fun doReload(oldState: State): State { val entities = loadFromDb().onEach { Log.d(tag, "Source: $it") } val sources = entities - .associateTo(mutableMapOf()) { uidOf(it) to loadEntity(it) } + .associateTo(mutableMapOf()) { it.uid to loadEntity(it) } sources.forEach syncName@{ (uid, src) -> val newName = src.loaded?.let(::realNameOf).takeIf { it != src.name } ?: return@syncName @@ -113,18 +119,20 @@ abstract class SourceManager( } val data = loadDataFromSources(sources) - return State(sources.toPersistentMap(), data) + return oldState.copy(data = data, sources = sources) } suspend fun reload() = dispatchAction("Full reload") { - doReload() + doReload(it) } private suspend fun loadFromDb(): List { - val all = dbGetAll() - if (all.isEmpty()) { - dbUpsert(defaultSource) - return listOf(defaultSource) + val all = dbGetAll().toMutableList() + val default = defaultSource + + if (all.none { it.uid == default.uid }) { + dbUpsert(default) + all += default } return all @@ -174,7 +182,7 @@ abstract class SourceManager( suspend fun reset() = dispatchAction("Reset") { state -> dbReset() state.sources.keys.forEach { directoryOf(it).deleteRecursively() } - doReload() + doReload(state.copy(updateErrors = emptyMap(), outdatedSources = emptySet())) } suspend fun remove(vararg sources: Source) = @@ -189,11 +197,17 @@ abstract class SourceManager( } val data = loadDataFromSources(currentSources) - State(currentSources.toPersistentMap(), data) + State( + data = data, + sources = currentSources, + updateErrors = state.updateErrors + .filter { (it, _) -> it in currentSources.keys }, + outdatedSources = state.outdatedSources.filterTo(mutableSetOf()) { it in currentSources.keys } + ) } suspend fun createLocal(createStream: suspend () -> InputStream) = - dispatchAction("Add local") { + dispatchAction("Add local") { state -> val entity = createEntity("", SourceInfo.Local) with(loadEntity(entity) as LocalSource) { try { @@ -209,40 +223,40 @@ abstract class SourceManager( } } - doReload() + doReload(state) } suspend fun createRemote(url: String, autoUpdate: Boolean) = dispatchAction("Add remote ($url)") { state -> val entity = createEntity("", SourceInfo.from(url), autoUpdate) val src = loadEntity(entity) as RemoteSource - update(src, force = true) - state.copy(sources = state.sources.put(src.uid, src)) + update(src) + state.copy(sources = state.sources.toMutableMap().also { it[src.uid] = src }) } - suspend fun reloadApiSources() = dispatchAction("Reload API sources") { + suspend fun reloadApiSources() = dispatchAction("Reload API sources") { state -> this@SourceManager.store.state.value.sources.values.filterIsInstance>() .forEach { src -> with(src) { deleteLocalFile() } updateDb(src.uid) { it.copy(versionHash = null) } } - doReload() + doReload(state) } suspend fun RemoteSource.setAutoUpdate(value: Boolean) = dispatchAction("Set auto update ($name, $value)") { state -> updateDb(uid) { it.copy(autoUpdate = value) } - val newSrc = (state.sources[uid] as? RemoteSource)?.copy(autoUpdate = value) + val newSrc = state.sources[uid]?.asRemoteOrNull?.copy(autoUpdate = value) ?: return@dispatchAction state - state.copy(sources = state.sources.put(uid, newSrc)) + state.copy(sources = state.sources.toMutableMap().also { it[uid] = newSrc }) } suspend fun update( vararg sources: RemoteSource, showToast: Boolean = false, - force: Boolean = false + force: Boolean = true ) { val uids = sources.map { it.uid }.toSet() store.dispatch(Update(showToast = showToast, force = force) { it.uid in uids }) @@ -254,8 +268,12 @@ abstract class SourceManager( /** * Updates all sources that should be automatically updated. */ - suspend fun updateCheck() = - store.dispatch(Update(force = prefs.allowMeteredNetworks.get()) { it.autoUpdate }) + suspend fun updateCheck(showToast: Boolean = false, force: Boolean = true) = store.dispatch( + Update( + showToast = showToast, + force = force || prefs.allowMeteredNetworks.get() + ) { it.autoUpdate } + ) private inner class Update( private val force: Boolean = false, @@ -263,8 +281,6 @@ abstract class SourceManager( private val showToast: Boolean = false, private val predicate: (source: RemoteSource) -> Boolean = { true }, ) : Action> { - private var attemptedMainApiUpdate = false - private suspend fun toast(@StringRes id: Int, vararg args: Any?) = withContext(Dispatchers.Main) { app.toast(app.getString(id, *args)) } @@ -272,25 +288,33 @@ abstract class SourceManager( override suspend fun ActionContext.execute( current: State - ) = coroutineScope { - // if (!networkInfo.isSafe(force)) { - // Log.d(tag, "Skipping update check because the network is down or metered.") - // return@coroutineScope current - // } + ) = supervisorScope { + val checkOnly = !force && !networkInfo.isUnmetered() - val updated = current.sources.values + val errors = current.updateErrors.toMutableMap() + val outdated = current.outdatedSources.toMutableSet() + + val results = current.sources.values .filterIsInstance>() .filter { predicate(it) } .also { targets -> - attemptedMainApiUpdate = targets.any { it.uid == 0 && it is APISource<*> } + // Clear errors for sources we are updating. + targets.forEach { src -> + errors.remove(src.uid) + outdated.remove(src.uid) + } } .map { - async { + async update@{ Log.d(tag, "Updating: ${it.name}") - val newVersion = with(it) { - if (redownload) downloadLatest() else update() - } ?: return@async null + val newVersion = it.runCatching { + when { + redownload -> downloadLatest() + checkOnly -> getUpdateInfo()?.version + else -> update() + } ?: return@update null + } it to newVersion } @@ -298,35 +322,60 @@ abstract class SourceManager( .awaitAll() .filterNotNull() .toMap() - if (updated.isEmpty()) { + if (results.isEmpty()) { if (showToast) toast(updateUnavailable) - return@coroutineScope current + return@supervisorScope current.copy( + updateErrors = errors, + outdatedSources = outdated + ) } - updated.forEach { (src, newVersionHash) -> - val name = src.loaded?.let(::realNameOf) ?: src.name + var hasErrors = false + results.forEach { (src, result) -> + result.getOrNull()?.let { newVersionHash -> + if (checkOnly) { + outdated.add(src.uid) + return@let + } - updateDb(src.uid) { - it.copy(versionHash = newVersionHash, name = name) + val name = src.loaded?.let(::realNameOf) ?: src.name + updateDb(src.uid) { + it.copy(versionHash = newVersionHash, name = name) + } + } + result.exceptionOrNull()?.let { + errors[src.uid] = it + Log.e(tag, "Failed to update source (${src.uid})", it) + hasErrors = true } } - if (showToast) toast(updateSuccess) - _updateError.value = null - if (attemptedMainApiUpdate) _apiOutageError.value = null - doReload() - } + when { + !showToast -> {} + hasErrors -> { + val error = errors.values.first() + toast(updateFailed, error) + } - override suspend fun catch(exception: Exception) { - Log.e(tag, "Failed to update", exception) - _updateError.value = exception - if (attemptedMainApiUpdate) _apiOutageError.value = exception - toast(updateFailed, exception.simpleMessage()) + else -> toast(updateSuccess) + } + doReload( + current.copy( + updateErrors = errors, + outdatedSources = outdated + ) + ) } } data class State( - val sources: PersistentMap> = persistentMapOf(), - val data: OUTPUT + val data: OUTPUT, + val sources: Map> = emptyMap(), + val updateErrors: Map = emptyMap(), + val outdatedSources: Set = emptySet(), ) + + interface DatabaseEntity { + val uid: Int + } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/domain/repository/DownloaderRepository.kt b/app/src/main/java/app/revanced/manager/domain/repository/DownloaderRepository.kt index c48e0b858..0c11d3bb9 100644 --- a/app/src/main/java/app/revanced/manager/domain/repository/DownloaderRepository.kt +++ b/app/src/main/java/app/revanced/manager/domain/repository/DownloaderRepository.kt @@ -3,9 +3,14 @@ package app.revanced.manager.domain.repository import android.annotation.SuppressLint import android.app.Application import android.content.Context -import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo +import android.content.res.Resources +import android.content.res.loader.ResourcesLoader +import android.content.res.loader.ResourcesProvider +import android.os.Build +import android.os.ParcelFileDescriptor import android.os.Parcelable +import androidx.annotation.RequiresApi import app.revanced.manager.R import app.revanced.manager.data.room.AppDatabase import app.revanced.manager.data.room.downloader.DownloaderEntity @@ -26,6 +31,7 @@ import app.revanced.manager.util.PM import dalvik.system.PathClassLoader import kotlinx.coroutines.flow.map import java.io.File +import java.lang.ref.WeakReference import java.lang.reflect.Modifier import app.revanced.manager.data.room.sources.Source as SourceInfo @@ -40,14 +46,6 @@ class DownloaderRepository( ) { private val dao = db.downloaderDao() - override val defaultSource = DownloaderEntity( - uid = 0, - name = "", - versionHash = null, - source = SourceInfo.API, - autoUpdate = false - ) - override suspend fun dbGetAll() = dao.all() override suspend fun dbGetProps(uid: Int) = dao.getProps(uid) override suspend fun dbUpsert(entity: DownloaderEntity) = dao.upsert(entity) @@ -55,17 +53,20 @@ class DownloaderRepository( override suspend fun dbReset() = dao.reset() private val loader = Loader { file -> + val dataDir = file.parentFile!!.resolve("data").also(File::mkdirs) val pkgInfo = pm.getPackageInfo(file) ?: error("Failed to get package info for $file") - loadPackage(pkgInfo) + loadPackage(pkgInfo, dataDir) } override fun loadEntity(entity: DownloaderEntity): Source = with(entity) { val file = directoryOf(uid).resolve("downloader.jar") + val actualName = + entity.name.ifEmpty { app.getString(if (uid == 0) R.string.auto_updates_dialog_downloaders else R.string.source_name_fallback) } return when (source) { - is SourceInfo.Local -> LocalSource(name, uid, null, file, loader) + is SourceInfo.Local -> LocalSource(actualName, uid, null, file, loader) is SourceInfo.API -> APISource( - name, + actualName, uid, versionHash, null, @@ -76,7 +77,7 @@ class DownloaderRepository( ) { getDownloaderUpdate() } is SourceInfo.Remote -> JsonSource( - name, + actualName, uid, versionHash, null, @@ -99,7 +100,6 @@ class DownloaderRepository( autoUpdate = props.autoUpdate ) - override fun uidOf(entity: DownloaderEntity) = entity.uid override fun realNameOf(loaded: DownloaderPackage) = loaded.name override val updateFailed = R.string.downloader_update_failed @@ -113,55 +113,46 @@ class DownloaderRepository( val downloaderSources = store.state.map { it.sources } val loadedDownloadersFlow = store.state.map { it.data } - // TODO: clear data for removed downloaders. - private val dataDir = app.getDir("downloaders_data", Context.MODE_PRIVATE) - fun findPackageByName(packageName: String) = store.state.value.sources.values.asSequence().mapNotNull { it.loaded } - .find { it.context.packageName == packageName } + .find { it.packageName == packageName } fun unwrapParceledData(data: ParceledDownloaderData): Pair { - val pkg = findPackageByName(data.downloaderPackageName) ?: throw Exception("Downloader package ${data.downloaderPackageName} is not available") + val pkg = findPackageByName(data.downloaderPackageName) + ?: throw Exception("Downloader package ${data.downloaderPackageName} is not available") val downloader = pkg.downloaders.firstOrNull { it.className == data.downloaderClassName } ?: throw Exception("No downloader with name ${data.downloaderClassName} found in ${data.downloaderPackageName}") return downloader to data.unwrapWith(pkg.classLoader) } - private val createApplicationContext by lazy { - val clazz = Context::class.java - clazz.getMethod("createApplicationContext", ApplicationInfo::class.java, Int::class.java) - } - - private fun loadPackage(packageInfo: PackageInfo): DownloaderPackage { + private fun loadPackage(packageInfo: PackageInfo, dataDir: File): DownloaderPackage { val packageName = packageInfo.packageName - - // The context is technically only necessary for resources. On API levels 30 and above, it would be better to use the proper APIs for dynamic resource loading. - val downloaderContext = createApplicationContext( - app, - packageInfo.applicationInfo, - 0 - ) as Context + val resources = pm.getResources(packageInfo) val classNamesResId = - @SuppressLint("DiscouragedApi") downloaderContext.resources.getIdentifier( + @SuppressLint("DiscouragedApi") resources.getIdentifier( CLASSES_RESOURCE_NAME, "array", - downloaderContext.packageName + packageName ) if (classNamesResId == 0) throw Exception("Class names resource not found (array/$CLASSES_RESOURCE_NAME)") - val classNames = downloaderContext.resources.getStringArray(classNamesResId) + val classNames = resources.getStringArray(classNamesResId) + val apkPath = packageInfo.applicationInfo!!.sourceDir val classLoader = - PathClassLoader(packageInfo.applicationInfo!!.sourceDir, app.classLoader) + PathClassLoader(apkPath, app.classLoader) val scopeImpl = object : Scope { override val hostPackageName = app.packageName - override val downloaderPackageName = downloaderContext.packageName - override val dataDir = - this@DownloaderRepository.dataDir.resolve(downloaderPackageName).also(File::mkdirs) + override val downloaderPackageName = packageName + override val dataDir = dataDir } + val resourceImpl = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) Api30ResourceImpl(File(apkPath)) + else OldResourceImpl(resources) + return DownloaderPackage( classNames.map { className -> val downloader = classLoader @@ -169,24 +160,63 @@ class DownloaderRepository( .getDownloaderBuilder() .build( scopeImpl = scopeImpl, - context = downloaderContext + context = app, + resources = resources ) LoadedDownloader( packageName, className, - downloaderContext.getString(downloader.name), + resources.getString(downloader.name), scopeImpl, downloader ) }, classLoader, - downloaderContext, + resourceImpl, + packageInfo.packageName, with(pm) { packageInfo.label() }, packageInfo.versionName.orEmpty() ) } + /** + * Provides resources for [app.revanced.manager.DownloaderActivity]. Has a better implementation on Android 11 and above. + */ + fun interface ResourceImpl { + fun apply(res: Resources): Resources? + } + + private class OldResourceImpl(val resources: Resources) : ResourceImpl { + @Suppress("DEPRECATION") + override fun apply(res: Resources) = + resources.also { it.updateConfiguration(res.configuration, res.displayMetrics) } + } + + @RequiresApi(Build.VERSION_CODES.R) + private class Api30ResourceImpl(private val file: File) : ResourceImpl { + private var weakRef: WeakReference? = null + + private fun getLoader(): ResourcesLoader { + weakRef?.get()?.let { return it } + + val provider = + ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY).use { pfd -> + ResourcesProvider.loadFromApk(pfd) + } + val loader = ResourcesLoader().apply { addProvider(provider) } + weakRef = WeakReference(loader) + + return loader + } + + override fun apply(res: Resources): Resources? { + val loader = getLoader() + res.addLoaders(loader) + return null + } + } + private companion object { const val CLASSES_RESOURCE_NAME = "app.revanced.manager.downloader.classes" diff --git a/app/src/main/java/app/revanced/manager/domain/repository/PatchBundleRepository.kt b/app/src/main/java/app/revanced/manager/domain/repository/PatchBundleRepository.kt index 053100b5b..bf13f7eea 100644 --- a/app/src/main/java/app/revanced/manager/domain/repository/PatchBundleRepository.kt +++ b/app/src/main/java/app/revanced/manager/domain/repository/PatchBundleRepository.kt @@ -42,14 +42,6 @@ class PatchBundleRepository( ) { private val dao = db.patchBundleDao() - override val defaultSource = PatchBundleEntity( - uid = 0, - name = "", - versionHash = null, - source = SourceInfo.API, - autoUpdate = false - ) - override val updateFailed = R.string.patches_download_fail override val updateSuccess = R.string.patches_update_success override val updateUnavailable = R.string.patches_update_unavailable @@ -64,7 +56,7 @@ class PatchBundleRepository( override fun loadEntity(entity: PatchBundleEntity): PatchBundleSource = with(entity) { val file = directoryOf(uid).resolve("patches.jar") val actualName = - entity.name.ifEmpty { app.getString(if (uid == 0) R.string.patches_name_default else R.string.patches_name_fallback) } + entity.name.ifEmpty { app.getString(if (uid == 0) R.string.patches_name_default else R.string.source_name_fallback) } return when (source) { is SourceInfo.Local -> LocalPatchBundle(actualName, uid, null, file, PatchBundleLoader) @@ -103,7 +95,6 @@ class PatchBundleRepository( autoUpdate = props.autoUpdate ) - override fun uidOf(entity: PatchBundleEntity) = entity.uid override fun realNameOf(loaded: PatchBundle) = loaded.manifestAttributes?.name override suspend fun loadDataFromSources(sources: MutableMap>) = loadMetadata(sources).toPersistentMap() diff --git a/app/src/main/java/app/revanced/manager/domain/sources/RemoteSource.kt b/app/src/main/java/app/revanced/manager/domain/sources/RemoteSource.kt index acb095468..fca38b254 100644 --- a/app/src/main/java/app/revanced/manager/domain/sources/RemoteSource.kt +++ b/app/src/main/java/app/revanced/manager/domain/sources/RemoteSource.kt @@ -48,13 +48,9 @@ sealed class RemoteSource( * Downloads the latest version regardless if there is a new update available. */ suspend fun ActionContext.downloadLatest() = download(getLatestInfo()) - + suspend fun ActionContext.getUpdateInfo() = getLatestInfo().takeUnless { hasInstalled() && it.version == versionHash } suspend fun ActionContext.update(): String? = withContext(Dispatchers.IO) { - val info = getLatestInfo() - if (hasInstalled() && info.version == versionHash) - return@withContext null - - download(info) + getUpdateInfo()?.let { download(it) } } companion object { diff --git a/app/src/main/java/app/revanced/manager/network/downloader/DownloaderPackage.kt b/app/src/main/java/app/revanced/manager/network/downloader/DownloaderPackage.kt index d07de8c87..6f2613411 100644 --- a/app/src/main/java/app/revanced/manager/network/downloader/DownloaderPackage.kt +++ b/app/src/main/java/app/revanced/manager/network/downloader/DownloaderPackage.kt @@ -1,11 +1,12 @@ package app.revanced.manager.network.downloader -import android.content.Context +import app.revanced.manager.domain.repository.DownloaderRepository data class DownloaderPackage( val downloaders: List, val classLoader: ClassLoader, - val context: Context, + val resourceImpl: DownloaderRepository.ResourceImpl, + val packageName: String, val name: String, val version: String ) \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/BundleInformationScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/BundleInformationScreen.kt index d978a49ea..31f573a93 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/BundleInformationScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/BundleInformationScreen.kt @@ -140,28 +140,23 @@ fun BundleInformationScreen( } }, actions = { - if (!src.isDefault) { - TooltipIconButton( - onClick = { showDeleteConfirmationDialog = true }, - tooltip = stringResource(R.string.delete), - ) { contentDescription -> - Icon( - Icons.Filled.Delete, - contentDescription - ) - } + if (!src.isDefault) TooltipIconButton( + onClick = { showDeleteConfirmationDialog = true }, + tooltip = stringResource(R.string.delete), + ) { contentDescription -> + Icon( + Icons.Filled.Delete, + contentDescription + ) } - val hasNetwork = remember { viewModel.networkInfo.isConnected() } - if (!isLocal && hasNetwork) { - TooltipIconButton( - onClick = viewModel::refresh, - tooltip = stringResource(R.string.refresh), - ) { contentDescription -> - Icon( - Icons.Filled.Refresh, - contentDescription - ) - } + if (!isLocal) TooltipIconButton( + onClick = viewModel::refresh, + tooltip = stringResource(R.string.refresh), + ) { contentDescription -> + Icon( + Icons.Filled.Refresh, + contentDescription + ) } }, scrollBehavior = scrollBehavior diff --git a/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt index 885cf3ada..18dea7dc3 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt @@ -125,6 +125,7 @@ fun DashboardScreen( val availablePatches by vm.availablePatches.collectAsStateWithLifecycle(0) val bundleDownloadError by vm.bundleDownloadError.collectAsStateWithLifecycle(null) val sourcesNotDownloaded by vm.sourcesNotDownloaded.collectAsStateWithLifecycle(false) + val sourceUpdatesAvailable by vm.sourceUpdatesAvailable.collectAsStateWithLifecycle(false) val managerAutoUpdates by vm.prefs.managerAutoUpdates.getAsState() val showManagerUpdateDialogOnLaunch by vm.prefs.showManagerUpdateDialogOnLaunch.getAsState() val disablePatchVersionCompatCheck by vm.prefs.disablePatchVersionCompatCheck.getAsState() @@ -405,7 +406,6 @@ fun DashboardScreen( patchesSourceEditMode = patchesSourceEditMode, onEnablePatchesSourceEditMode = { patchesSourceEditMode = true }, onAddBundleClick = { - vm.cancelSourceSelection() showAddBundleDialog = true } ) @@ -440,15 +440,22 @@ fun DashboardScreen( ) } } else null, - if (sourcesNotDownloaded && bundleDownloadError == null) { + if (sourceUpdatesAvailable) { + { + NotificationCard( + type = NotificationCardType.WARNING, + icon = Icons.Outlined.Refresh, + text = stringResource(R.string.banner_sources_not_updated_description), + onClick = vm::downloadSources + ) + } + } else if (sourcesNotDownloaded && bundleDownloadError == null) { { NotificationCard( type = NotificationCardType.WARNING, icon = Icons.Outlined.Refresh, text = stringResource(R.string.banner_sources_not_downloaded_description), - onClick = { - vm.downloadSources() - } + onClick = vm::downloadSources ) } } else null, diff --git a/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt index 8ddd510bd..615de6aa1 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt @@ -76,7 +76,6 @@ fun SelectedAppInfoScreen( ) { val resources = LocalResources.current val networkInfo = koinInject() - val networkConnected = remember { networkInfo.isConnected() } val networkMetered = remember { !networkInfo.isUnmetered() } val packageName = vm.selectedApp.packageName @@ -131,8 +130,8 @@ fun SelectedAppInfoScreen( derivedStateOf { val selectedVersion = vm.selectedApp.version ?: return@derivedStateOf false allowIncompatiblePatches && - strictVersionOptions.versions.isNotEmpty() && - selectedVersion !in strictVersionOptions.versions + strictVersionOptions.versions.isNotEmpty() && + selectedVersion !in strictVersionOptions.versions } } @@ -359,26 +358,12 @@ fun SelectedAppInfoScreen( val needsInternet = vm.selectedApp.let { it is SelectedApp.Search || it is SelectedApp.Download } - when { - !needsInternet -> {} - !networkConnected -> { - NotificationCard( - type = NotificationCardType.WARNING, - icon = Icons.Outlined.WarningAmber, - text = stringResource(R.string.network_unavailable_warning), - onDismiss = null - ) - } - - networkMetered -> { - NotificationCard( - type = NotificationCardType.WARNING, - icon = Icons.Outlined.WarningAmber, - text = stringResource(R.string.network_metered_warning), - onDismiss = null - ) - } - } + if (needsInternet && networkMetered) NotificationCard( + type = NotificationCardType.WARNING, + icon = Icons.Outlined.WarningAmber, + text = stringResource(R.string.network_metered_warning), + onDismiss = null + ) } } } @@ -562,8 +547,10 @@ private fun AppSourceSelectorDialog( LazyColumn { item(key = "auto") { val hasDownloader = downloaders.isNotEmpty() - val hasDownloaded = downloadedApps.any { app -> requiredVersion == null || app.version == requiredVersion } - val hasAutoSource = hasDownloader || hasDownloaded || autoSelection is SelectedApp.Installed + val hasDownloaded = + downloadedApps.any { app -> requiredVersion == null || app.version == requiredVersion } + val hasAutoSource = + hasDownloader || hasDownloaded || autoSelection is SelectedApp.Installed ListItem( modifier = Modifier .clickable(enabled = canSelect && hasAutoSource) { onSelectAuto() } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt index 2b43551b3..1510e4f46 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt @@ -137,11 +137,6 @@ fun AboutSettingsScreen( stringResource(R.string.contributors), stringResource(R.string.contributors_description), third = nav@{ - if (!viewModel.isConnected) { - context.toast(resources.getString(R.string.no_network_toast)) - return@nav - } - navigate(Settings.Contributors) } ), diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/DeveloperSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/DeveloperSettingsScreen.kt index 4b48764be..29361b0b7 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/DeveloperSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/DeveloperSettingsScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.outlined.Api +import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.PostAdd import androidx.compose.material.icons.outlined.Restore import androidx.compose.material.icons.outlined.WorkOutline @@ -138,7 +139,17 @@ fun DeveloperSettingsScreen( ) SettingsListItem( headlineContent = stringResource(R.string.patches_reset), - onClick = vm::redownloadBundles + onClick = vm::resetBundles + ) + } + + ListSection( + title = stringResource(R.string.downloaders), + leadingContent = { Icon(Icons.Outlined.Download, contentDescription = null, modifier = Modifier.size(18.dp)) } + ) { + SettingsListItem( + headlineContent = stringResource(R.string.downloaders_reset), + onClick = vm::resetDownloaders ) } } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/DownloadersInfoScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/DownloadersInfoScreen.kt index 4ec2b109c..7c4faa90c 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/DownloadersInfoScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/DownloadersInfoScreen.kt @@ -128,9 +128,6 @@ fun DownloaderInfoScreen( } remote?.let { - val hasNetwork = remember { viewModel.networkInfo.isConnected() } - if (!hasNetwork) return@let - TooltipIconButton( onClick = { viewModel.updateDownloader(it) }, enabled = !isDeleting, diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/update/UpdatesSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/update/UpdatesSettingsScreen.kt index c480dc0d6..3527c1638 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/update/UpdatesSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/update/UpdatesSettingsScreen.kt @@ -117,10 +117,6 @@ fun UpdatesSettingsScreen( return@launch } - if (!vm.isConnected) { - context.toast(resources.getString(R.string.no_network_toast)) - return@launch - } checkingForUpdate = true try { val version = vm.checkForUpdates() @@ -202,13 +198,7 @@ fun UpdatesSettingsScreen( } Spacer(modifier = Modifier.height(4.dp)) Button( - onClick = { - if (!vm.isConnected) { - context.toast(resources.getString(R.string.no_network_toast)) - } else { - onChangelogClick() - } - }, + onClick = onChangelogClick, colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, contentColor = MaterialTheme.colorScheme.onSurface diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/AboutViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/AboutViewModel.kt index 4fabdc213..23b5864d7 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/AboutViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/AboutViewModel.kt @@ -7,7 +7,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.network.api.ReVancedAPI import app.revanced.manager.network.dto.ReVancedDonationLink @@ -27,7 +26,6 @@ import kotlinx.coroutines.withContext class AboutViewModel( private val reVancedAPI: ReVancedAPI, - private val network: NetworkInfo, prefs: PreferencesManager, ) : ViewModel() { var socials by mutableStateOf(emptyList()) @@ -36,16 +34,11 @@ class AboutViewModel( private set var donate by mutableStateOf(null) private set - val isConnected: Boolean - get() = network.isConnected() val showDeveloperSettings = prefs.showDeveloperSettings init { viewModelScope.launch { - if (!isConnected) { - return@launch - } withContext(Dispatchers.IO) { reVancedAPI.getInfo().getOrNull() }?.let { diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/AnnouncementsViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/AnnouncementsViewModel.kt index b6d7675da..2432a01f2 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/AnnouncementsViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/AnnouncementsViewModel.kt @@ -2,7 +2,6 @@ package app.revanced.manager.ui.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.AnnouncementRepository import app.revanced.manager.network.dto.ReVancedAnnouncement @@ -12,8 +11,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toInstant import kotlin.time.Clock data class AnnouncementSections( @@ -26,7 +23,6 @@ data class AnnouncementSections( class AnnouncementsViewModel( private val announcementRepository: AnnouncementRepository, - private val network: NetworkInfo, private val preferences: PreferencesManager ) : ViewModel() { private val allAnnouncements = MutableStateFlow?>(null) @@ -92,11 +88,6 @@ class AnnouncementsViewModel( private fun loadData() { viewModelScope.launch { - if (!network.isConnected()) { - allAnnouncements.value = emptyList() - return@launch - } - withContext(Dispatchers.IO) { announcementRepository.getAnnouncements()?.let { allAnnouncements.value = it diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/BundleInformationViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/BundleInformationViewModel.kt index 9130a2bf7..3b7ca374c 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/BundleInformationViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/BundleInformationViewModel.kt @@ -2,7 +2,6 @@ package app.revanced.manager.ui.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.PatchBundleRepository import app.revanced.manager.domain.sources.Extensions.asRemoteOrNull @@ -14,7 +13,6 @@ import org.koin.core.component.get class BundleInformationViewModel(uid: Int) : ViewModel(), KoinComponent { private val patchBundleRepository: PatchBundleRepository = get() - val networkInfo: NetworkInfo = get() val prefs: PreferencesManager = get() var bundle = patchBundleRepository.sources.map { sources -> sources.find { it.uid == uid } } @@ -26,7 +24,7 @@ class BundleInformationViewModel(uid: Int) : ViewModel(), KoinComponent { fun refresh() = viewModelScope.launch { bundle.first()?.asRemoteOrNull?.let { - patchBundleRepository.update(it, showToast = true, force = true) + patchBundleRepository.update(it, showToast = true) } } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/BundleListViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/BundleListViewModel.kt index 6b589a915..f1276b6b4 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/BundleListViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/BundleListViewModel.kt @@ -53,8 +53,7 @@ class BundleListViewModel : ViewModel(), KoinComponent { Event.UPDATE_SELECTED -> viewModelScope.launch { patchBundleRepository.update( *getSelectedSources().filterIsInstance().toTypedArray(), - showToast = true, - force = true + showToast = true ) } } @@ -66,7 +65,7 @@ class BundleListViewModel : ViewModel(), KoinComponent { fun update(src: PatchBundleSource) = viewModelScope.launch { if (src !is RemotePatchBundle) return@launch - patchBundleRepository.update(src, showToast = true, force = true) + patchBundleRepository.update(src, showToast = true) } enum class Event { diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt index b7a5ba411..77274461b 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt @@ -5,30 +5,24 @@ import android.app.Application import android.content.ContentResolver import android.net.Uri import android.os.Build -import android.os.PowerManager import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.core.content.getSystemService import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.revanced.manager.R -import app.revanced.manager.data.platform.NetworkInfo -import app.revanced.manager.domain.sources.Extensions.asRemoteOrNull import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.AnnouncementRepository import app.revanced.manager.domain.repository.DownloaderRepository import app.revanced.manager.domain.repository.ManagerUpdateRepository import app.revanced.manager.domain.repository.PatchBundleRepository import app.revanced.manager.network.dto.ReVancedAnnouncement -import app.revanced.manager.network.dto.ReVancedAsset import app.revanced.manager.util.PM import app.revanced.manager.util.uiSafe import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -38,7 +32,6 @@ class DashboardViewModel( private val downloaderRepository: DownloaderRepository, private val announcementRepository: AnnouncementRepository, private val managerUpdateRepository: ManagerUpdateRepository, - private val networkInfo: NetworkInfo, val prefs: PreferencesManager, private val pm: PM, ) : ViewModel() { @@ -46,15 +39,19 @@ class DashboardViewModel( patchBundleRepository.bundleInfoFlow.map { it.values.sumOf { bundle -> bundle.patches.size } } val bundleDownloadError = patchBundleRepository.apiOutageError private val contentResolver: ContentResolver = app.contentResolver - private val powerManager = app.getSystemService()!! val availableManagerUpdate = managerUpdateRepository.availableVersion val sourcesNotDownloaded = patchBundleRepository.bundleInfoFlow.map { it.isEmpty() } + val sourceUpdatesAvailable = combine( + patchBundleRepository.hasOutdated, + downloaderRepository.hasOutdated + ) { patches, downloaders -> patches || downloaders } fun downloadSources() = viewModelScope.launch(Dispatchers.Default) { - patchBundleRepository.updateCheck() - downloaderRepository.updateCheck() + arrayOf(patchBundleRepository, downloaderRepository).forEach { + it.updateCheck(showToast = true) + } } /** @@ -68,9 +65,6 @@ class DashboardViewModel( var unreadAnnouncement by mutableStateOf(null) private set - private val bundleListEventsChannel = Channel() - val bundleListEventsFlow = bundleListEventsChannel.receiveAsFlow() - init { viewModelScope.launch { checkForManagerUpdates() @@ -79,7 +73,7 @@ class DashboardViewModel( } private suspend fun checkForManagerUpdates() { - if (!prefs.managerAutoUpdates.get() || !networkInfo.isConnected()) return + if (!prefs.managerAutoUpdates.get()) return uiSafe(app, R.string.failed_to_check_updates, "Failed to check for updates") { managerUpdateRepository.refreshAvailableVersion() @@ -123,16 +117,9 @@ class DashboardViewModel( } } - private fun sendEvent(event: BundleListViewModel.Event) { - viewModelScope.launch { bundleListEventsChannel.send(event) } - } - - fun cancelSourceSelection() = sendEvent(BundleListViewModel.Event.CANCEL) - fun updateSources() = sendEvent(BundleListViewModel.Event.UPDATE_SELECTED) - fun deleteSources() = sendEvent(BundleListViewModel.Event.DELETE_SELECTED) - fun deleteSource(uid: Int) = viewModelScope.launch { - val source = patchBundleRepository.sources.first().firstOrNull { it.uid == uid } ?: return@launch + val source = + patchBundleRepository.sources.first().firstOrNull { it.uid == uid } ?: return@launch patchBundleRepository.remove(source) } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/DeveloperOptionsViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/DeveloperOptionsViewModel.kt index b7a4f834e..f58c12958 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/DeveloperOptionsViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/DeveloperOptionsViewModel.kt @@ -40,6 +40,10 @@ class DeveloperOptionsViewModel( patchBundleRepository.reset() } + fun resetDownloaders() = viewModelScope.launch { + downloaderRepository.reset() + } + fun resetOnboarding() = viewModelScope.launch { prefs.completedOnboarding.update(false) app.toast(app.getString(R.string.sideeffect_restart)) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/DownloadsViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/DownloadsViewModel.kt index 8da2aff41..63d20cd7b 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/DownloadsViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/DownloadsViewModel.kt @@ -105,7 +105,7 @@ class DownloadsViewModel( fun updateDownloader(src: RemoteSource) = viewModelScope.launch { try { isUpdatingDownloader = true - downloaderRepository.update(src, showToast = true, force = true) + downloaderRepository.update(src, showToast = true) } finally { isUpdatingDownloader = false } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt index 86109c7eb..62d0f412b 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt @@ -9,7 +9,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.revanced.manager.R import app.revanced.manager.data.room.apps.installed.InstallType -import app.revanced.manager.domain.sources.Extensions.asRemoteOrNull import app.revanced.manager.domain.manager.KeystoreManager import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.DownloadedAppRepository @@ -155,17 +154,6 @@ class MainViewModel( settings.experimentalPatchesEnabled?.let { allowExperimental -> prefs.disablePatchVersionCompatCheck.update(allowExperimental) } - settings.patchesAutoUpdate?.let { autoUpdate -> - with(patchBundleRepository) { - sources - .first() - .find { it.uid == 0 } - ?.asRemoteOrNull - ?.setAutoUpdate(autoUpdate) - - updateCheck() - } - } settings.patchesChangeEnabled?.let { disableSelectionWarning -> prefs.disableSelectionWarning.update(disableSelectionWarning) } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/OnboardingViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/OnboardingViewModel.kt index 13e6315a2..648e44fd0 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/OnboardingViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/OnboardingViewModel.kt @@ -9,7 +9,6 @@ import androidx.compose.runtime.setValue import androidx.core.app.NotificationManagerCompat import androidx.core.content.getSystemService import androidx.lifecycle.ViewModel -import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.domain.sources.Extensions.asRemoteOrNull import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.DownloaderRepository @@ -32,7 +31,6 @@ class OnboardingViewModel( private val pm: PM, private val downloaderRepository: DownloaderRepository, private val patchBundleRepository: PatchBundleRepository, - private val networkInfo: NetworkInfo, ) : ViewModel() { private val powerManager = app.getSystemService()!! @@ -41,8 +39,8 @@ class OnboardingViewModel( } val apiUrl = prefs.api.default - val hasNetworkError = combine(apps, patchBundleRepository.updateError) { apps, updateError -> - apps == null && (!networkInfo.isConnected() || updateError != null) + val hasNetworkError = combine(apps, patchBundleRepository.updateErrors) { apps, updateErrors -> + apps == null && updateErrors.isNotEmpty() } val suggestedVersions = patchBundleRepository.suggestedVersions @@ -94,7 +92,7 @@ class OnboardingViewModel( ?.asRemoteOrNull ?: return@with src.setAutoUpdate(patchesEnabled) - if (networkInfo.isConnected()) update(src) + update(src) } with(downloaderRepository) { @@ -103,7 +101,7 @@ class OnboardingViewModel( ?.asRemoteOrNull ?: return@with src.setAutoUpdate(downloadersEnabled) - if (networkInfo.isConnected()) update(src) + update(src) } } @@ -122,5 +120,4 @@ class OnboardingViewModel( OnboardingStep.Updates -> OnboardingStep.Permissions OnboardingStep.Apps -> OnboardingStep.Updates } - } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdateViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdateViewModel.kt index c9f1051b3..15b1afaad 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdateViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdateViewModel.kt @@ -82,7 +82,7 @@ class UpdateViewModel( uiSafe(app, R.string.failed_to_download_update, "Failed to download update") { val release = releaseInfo!! withContext(Dispatchers.IO) { - if (!networkInfo.isSafe(false) && !ignoreInternetCheck) { + if (!ignoreInternetCheck && !networkInfo.isUnmetered()) { showInternetCheckDialog = true } else { state = State.DOWNLOADING diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdatesSettingsViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdatesSettingsViewModel.kt index e0aa6e348..55394055f 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdatesSettingsViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdatesSettingsViewModel.kt @@ -20,9 +20,6 @@ class UpdatesSettingsViewModel( val useManagerPrereleases = prefs.useManagerPrereleases val availableManagerUpdate = managerUpdateRepository.availableVersion - val isConnected: Boolean - get() = network.isConnected() - suspend fun checkForUpdates(): String? { var availableVersion: String? = null diff --git a/app/src/main/java/app/revanced/manager/util/PM.kt b/app/src/main/java/app/revanced/manager/util/PM.kt index a1d5a06ff..6d3b7852d 100644 --- a/app/src/main/java/app/revanced/manager/util/PM.kt +++ b/app/src/main/java/app/revanced/manager/util/PM.kt @@ -4,11 +4,9 @@ import android.annotation.SuppressLint import android.app.Application import android.content.Intent import android.content.pm.PackageInfo -import android.content.pm.PackageManager import android.content.pm.PackageManager.PackageInfoFlags import android.content.pm.PackageManager.NameNotFoundException import androidx.core.content.pm.PackageInfoCompat -import android.content.pm.Signature import android.os.Build import android.os.Parcelable import androidx.compose.runtime.Immutable @@ -99,12 +97,6 @@ class PM( else app.packageManager.getInstalledPackages(flags) - fun getPackagesWithFeature(feature: String) = - getInstalledPackages(PackageManager.GET_CONFIGURATIONS) - .filter { pkg -> - pkg.reqFeatures?.any { it.name == feature } ?: false - } - fun getPackageInfo(packageName: String, flags: Int = 0): PackageInfo? = try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) @@ -128,49 +120,11 @@ class PM( return pkgInfo } - @SuppressLint("InlinedApi") - fun getApkSignature(file: File): Signature? { - val path = file.absolutePath - val pkgInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) - app.packageManager.getPackageArchiveInfo( - path, - PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES.toLong()) - ) - else - app.packageManager.getPackageArchiveInfo( - path, - PackageManager.GET_SIGNING_CERTIFICATES - ) - - return pkgInfo?.signingInfo?.let { signingInfo -> - if (signingInfo.hasMultipleSigners()) { - val managerSignature = getManagerSignature() - signingInfo.apkContentsSigners.firstOrNull { it == managerSignature } - ?: signingInfo.apkContentsSigners.lastOrNull() - } else { - signingInfo.signingCertificateHistory.lastOrNull() - } - } - } - - fun getManagerSignature(): Signature = getSignature(app.packageName) - fun PackageInfo.label() = this.applicationInfo!!.loadLabel(app.packageManager).toString() + fun getResources(packageInfo: PackageInfo) = app.packageManager.getResourcesForApplication(packageInfo.applicationInfo!!) fun getVersionCode(packageInfo: PackageInfo) = PackageInfoCompat.getLongVersionCode(packageInfo) - fun getSignature(packageName: String): Signature = - // Get the last signature from the list because we want the newest one if SigningInfo.getSigningCertificateHistory() was used. - PackageInfoCompat.getSignatures(app.packageManager, packageName).last() - - @SuppressLint("InlinedApi") - fun hasSignature(packageName: String, signature: ByteArray) = PackageInfoCompat.hasSignatures( - app.packageManager, - packageName, - mapOf(signature to PackageManager.CERT_INPUT_RAW_X509), - false - ) - suspend fun uninstallPackage(pkg: String, config: UninstallParametersDsl.() -> Unit = {}) = withContext(Dispatchers.IO) { uninstaller.createSession(pkg) { confirmation = Confirmation.IMMEDIATE diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml index bcaed228f..5367745e4 100644 --- a/app/src/main/res/values-ar-rSA/strings.xml +++ b/app/src/main/res/values-ar-rSA/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" تعذر تحميل التعديلات. انقر لعرض الخطأ لم يتم تنزيل التصحيحات. التعديلات - غير مُسمى + غير مُسمى خطأ Android 11 يجب منح إذن تثبيت التطبيق مسبقًا لتجنب خطأ في نظام Android 11 سيؤثر سلبًا على تجربة المستخدم. لا يوجد اتصال بالإنترنت متاح diff --git a/app/src/main/res/values-az-rAZ/strings.xml b/app/src/main/res/values-az-rAZ/strings.xml index 99e5a4e57..e97220666 100644 --- a/app/src/main/res/values-az-rAZ/strings.xml +++ b/app/src/main/res/values-az-rAZ/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Yamaqlar yüklənə bilmədi. Xətanı görmək üçün klikləyin Yamaqlar yüklənməyib. Yamaqlar - Adsız + Adsız Android 11 səhvi Tətbiqin quraşdırılması icazəsi, Android 11 sistemində istifadəçi təcrübəsinə mənfi təsir göstərəcək bir səhvin qarşısını almaq üçün əvvəlcədən verilməlidir. İnternet bağlantısı yoxdur diff --git a/app/src/main/res/values-be-rBY/strings.xml b/app/src/main/res/values-be-rBY/strings.xml index 83d2e517e..657147b73 100644 --- a/app/src/main/res/values-be-rBY/strings.xml +++ b/app/src/main/res/values-be-rBY/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Патчы не ўдалося загрузіць. Націсніце, каб праглядзець памылку Выпраўленні не спампаваны. Патчы - Без назвы + Без назвы Памылка Android 11 Дазвол на ўстаноўку праграм павінен быць выдадзены загадзя, каб пазбегнуць памылкі ў сістэме Android 11, якая негатыўна паўплывае на зручнасць карыстання. Няма падключэння да інтэрнэту diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index fcfe3cb31..a0af6b90e 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Пачовете не можаха да бъдат заредени. Кликнете, за да видите грешката Пачовете не са изтеглени. Пачове - Без име + Без име Бъг в Android 11 Разрешението за инсталиране на приложението трябва да бъде предоставено предварително, за да се избегне бъг в системата на Android 11, който ще повлияе негативно на потребителското изживяване. Няма налична интернет връзка diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml index 9ccbc4a14..5b31502da 100644 --- a/app/src/main/res/values-bn-rBD/strings.xml +++ b/app/src/main/res/values-bn-rBD/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" প্যাচ লোড করা যায়নি। ত্রুটি দেখতে ক্লিক করুন প্যাচগুলি ডাউনলোড করা হয়নি। প্যাচগুলি - নামহীন + নামহীন Android 11 বাগ Android 11 সিস্টেমে একটি বাগ এড়াতে অ্যাপ ইনস্টলেশনের অনুমতি আগে থেকেই দিতে হবে, যা ব্যবহারকারীর অভিজ্ঞতার উপর নেতিবাচক প্রভাব ফেলবে। কোনো ইন্টারনেট সংযোগ উপলব্ধ নেই diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 52eed60f8..fcdc12962 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Záplaty nebylo možné načíst. Kliknutím zobrazíte chybu. Záplaty nebyly staženy. Záplaty - Nepojmenováno + Nepojmenováno Chyba Androidu 11 Povolení k instalaci aplikací musí být uděleno předem, aby se předešlo chybě v systému Android 11, která by negativně ovlivnila uživatelský zážitek. Připojení k internetu není dostupné diff --git a/app/src/main/res/values-da-rDK/strings.xml b/app/src/main/res/values-da-rDK/strings.xml index 1878ac088..f627cca11 100644 --- a/app/src/main/res/values-da-rDK/strings.xml +++ b/app/src/main/res/values-da-rDK/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Patches kunne ikke indlæses. Klik for at se fejlen Patches er ikke blevet downloadet. Patches - Unavngivet + Unavngivet Android 11-fejl App-installationstilladelsen skal gives på forhånd for at undgå en fejl i Android 11-systemet, som vil påvirke brugeroplevelsen negativt. Ingen internetforbindelse tilgængelig diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 3bd4304f1..0ff013d7a 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Patches konnten nicht geladen werden. Klicken Sie hier, um den Fehler anzuzeigen. Patches wurden nicht heruntergeladen. Patches - Unbenannt + Unbenannt Android 11 Fehler Die Berechtigung zur App-Installation muss im Voraus erteilt werden, um einen Fehler im Android 11-System zu vermeiden, der die Benutzererfahrung negativ beeinflusst. Keine Internetverbindung verfügbar diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml index a3b15fed9..ed3d7462a 100644 --- a/app/src/main/res/values-el-rGR/strings.xml +++ b/app/src/main/res/values-el-rGR/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Δεν ήταν δυνατή η φόρτωση των τροποποιήσεων. Πατήστε για να δείτε το σφάλμα Οι ενημερώσεις δεν έχουν ληφθεί. Τροποποιήσεις - Χωρίς Όνομα + Χωρίς Όνομα Σφάλμα Android 11 Η άδεια εγκατάστασης εφαρμογών πρέπει να χορηγηθεί εκ των προτέρων για να αποφευχθεί ένα σφάλμα του συστήματος Android 11 που θα επηρεάσει αρνητικά την εμπειρία του χρήστη. Δεν υπάρχει διαθέσιμη σύνδεση στο διαδίκτυο diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 7e7a73403..e0d376c68 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" No se pudieron cargar los parches. Haz clic para ver el error Los parches no se han descargado. Parches - Sin nombre + Sin nombre Error de Android 11 El permiso de instalación de la aplicación debe concederse con antelación para evitar un error en el sistema Android 11 que afectará negativamente a la experiencia del usuario. No hay conexión a internet disponible diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 41a968370..465fedec1 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Paikasid ei saanud laadida. Vea vaatamiseks klõpsake. Paigad pole alla laaditud. Paigad - Nimetu + Nimetu Android 11 viga Rakenduse installimise luba tuleb eelnevalt anda, et vältida Android 11 süsteemi viga, mis mõjutab negatiivselt kasutajakogemust. Internetiühendus puudub diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 8072faa72..e5c27a612 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Paikkauksia ei voitu ladata. Napauta nähdäksesi virheen Päivityksiä ei ole ladattu. Paikkaukset - Nimetön + Nimetön Android 11 -virhe Sovelluksen asennusoikeus on myönnettävä etukäteen, jotta vältetään Android 11 -järjestelmän virhe, joka vaikuttaa negatiivisesti käyttökokemukseen. Internet-yhteyttä ei ole käytettävissä diff --git a/app/src/main/res/values-fil-rPH/strings.xml b/app/src/main/res/values-fil-rPH/strings.xml index 1e0eb3813..6e9961044 100644 --- a/app/src/main/res/values-fil-rPH/strings.xml +++ b/app/src/main/res/values-fil-rPH/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Hindi ma-load ang mga patch. Mag-click upang tingnan ang error Hindi pa na-download ang mga patch. Mga patch - Walang pangalan + Walang pangalan Bug sa Android 11 Ang pahintulot sa pag-install ng app ay dapat ibigay nang maaga upang maiwasan ang isang bug sa sistema ng Android 11 na magdulot ng negatibong epekto sa karanasan ng gumagamit. Walang available na koneksyon sa internet diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index 44cd7153f..7dfd0c196 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Les patchs n\'ont pas pu être chargés, appuyez pour voir l\'erreur Les patchs n\'ont pas été téléchargés. Patchs - Sans nom + Sans nom Bug Android 11 L\'autorisation d\'installation d\'applications doit être accordée à l\'avance pour contourner un bug système dans Android 11 qui affecterait négativement l\'expérience utilisateur. Aucune connexion Internet disponible diff --git a/app/src/main/res/values-ga-rIE/strings.xml b/app/src/main/res/values-ga-rIE/strings.xml index e5ee67bd5..196baa627 100644 --- a/app/src/main/res/values-ga-rIE/strings.xml +++ b/app/src/main/res/values-ga-rIE/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Níorbh fhéidir paistí a luchtú. Cliceáil chun an earráid a fheiceáil Níor íoslódáladh na paistí. Paistí - Gan ainm + Gan ainm Fabht Android 11 Ní mór an cead suiteála aip a dheonú roimh ré chun fabht sa chóras Android 11 a sheachaint a dhéanfaidh difear diúltach d\'eispéireas an úsáideora. Níl aon nasc idirlín ar fáil diff --git a/app/src/main/res/values-hr-rHR/strings.xml b/app/src/main/res/values-hr-rHR/strings.xml index 4f012df1b..890c240cc 100644 --- a/app/src/main/res/values-hr-rHR/strings.xml +++ b/app/src/main/res/values-hr-rHR/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Patchevi se nisu mogli učitati. Kliknite za prikaz pogreške Patchevi nisu preuzeti. Patchevi - Neimenovano + Neimenovano Greška Androida 11 Automatski Koristite instaliranu aplikaciju, zatim preuzeti APK, a zatim dostupne programe za preuzimanje diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index 5f5b97520..0eba8a065 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" A javítások betöltése sikertelen. Kattintson a hibák megtekintéséhez. A javítások nincsenek letöltve. Javítások - Névtelen + Névtelen Android 11 hiba Az alkalmazástelepítési engedélyt előre meg kell adni, hogy elkerülhető legyen egy Android 11 rendszerhiba, amely negatívan befolyásolná a felhasználói élményt. Nincs elérhető internetkapcsolat diff --git a/app/src/main/res/values-hy-rAM/strings.xml b/app/src/main/res/values-hy-rAM/strings.xml index 2224882fd..5b2963e81 100644 --- a/app/src/main/res/values-hy-rAM/strings.xml +++ b/app/src/main/res/values-hy-rAM/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Կարկատանները չհաջողվեց բեռնել։ Կտտացրեք՝ սխալը դիտելու համար Կարկատանները չեն ներբեռնվել։ Կարկատաններ - Անանուն + Անանուն Android 11-ի սխալ Հավելվածի տեղադրման թույլտվությունը պետք է տրվի նախապես՝ խուսափելու համար Android 11 համակարգի սխալից, որը բացասաբար կազդի օգտագործողի փորձի վրա։ Ինտերնետ կապ չկա diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml index 06e8509fc..e34598462 100644 --- a/app/src/main/res/values-in-rID/strings.xml +++ b/app/src/main/res/values-in-rID/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Tambalan tidak dapat dimuat. Klik untuk melihat kesalahan Tambalan belum diunduh. Tambalan - Tanpa nama + Tanpa nama Bug Android 11 Izin pemasangan aplikasi harus diberikan sebelumnya untuk menghindari bug di sistem Android 11 yang akan berdampak negatif pada pengalaman pengguna. Tidak ada koneksi internet yang tersedia diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index 19f9e98b3..c0a5154c6 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Le patch non sono state caricate. Fai clic per visualizzare l\'errore Le patch non sono state scaricate. Patch - Senza nome + Senza nome Bug di Android 11 L\'autorizzazione all\'installazione dell\'app deve essere concessa in anticipo per evitare un bug nel sistema Android 11 che influenzerà negativamente l\'esperienza utente. Nessuna connessione internet disponibile diff --git a/app/src/main/res/values-iw-rIL/strings.xml b/app/src/main/res/values-iw-rIL/strings.xml index 19aae8dbb..3f8e8ab37 100644 --- a/app/src/main/res/values-iw-rIL/strings.xml +++ b/app/src/main/res/values-iw-rIL/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" לא ניתן לטעון טלאים. לחץ כדי להציג את השגיאה הטלאים לא הורדו. טלאים - ללא שם + ללא שם באג אנדרואיד 11 יש להעניק את הרשאת התקנת היישומים מראש כדי למנוע באג במערכת אנדרואיד 11 שישפיע לרעה על חווית המשתמש. אין חיבור אינטרנט זמין diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index a1925abec..6eccc2779 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" パッチを読み込めませんでした。クリックしてエラーを表示 パッチがダウンロードされていません。 パッチ - 名無し + 名無し Android 11 のバグ アプリのインストール権限は、Android 11 システムのバグがユーザーエクスペリエンスに悪影響を与えるのを避けるため、事前に付与しておく必要があります。 インターネット接続がありません diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 166b97ff0..a29d31f05 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" 패치를 불러올 수 없습니다. 오류를 보려면 여기를 탭하세요 패치가 다운로드되지 않았습니다. 패치 - 이름 없음 + 이름 없음 Android 11 버그 사용자 환경에 부정적인 영향을 미치는 Android 11 시스템 버그를 방지하려면 앱 설치 권한을 미리 부여해야 합니다. 인터넷에 연결할 수 없습니다 diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index 57c31868a..1a612b825 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Taisymų nepavyko įkelti. Spustelėkite, kad peržiūrėtumėte klaidą. Lopai nebuvo atsisiųsti. Taisymai - Be pavadinimo + Be pavadinimo „Android 11“ klaida Programos diegimo leidimas turi būti suteiktas iš anksto, kad būtų išvengta „Android 11“ sistemos klaidos, kuri neigiamai paveiks naudotojo patirtį. Nėra interneto ryšio diff --git a/app/src/main/res/values-lv-rLV/strings.xml b/app/src/main/res/values-lv-rLV/strings.xml index d037f64a5..f672be9d6 100644 --- a/app/src/main/res/values-lv-rLV/strings.xml +++ b/app/src/main/res/values-lv-rLV/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Ielāpus nevarēja ielādēt. Noklikšķiniet, lai apskatītu kļūdu Plāksteri nav lejupielādēti. Ielāpi - Bez nosaukuma + Bez nosaukuma Android 11 kļūda Lietotņu instalēšanas atļauja jāpiešķir iepriekš, lai izvairītos no kļūdas Android 11 sistēmā, kas negatīvi ietekmēs lietotāja pieredzi. Nav pieejams interneta savienojums diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml index bc129fc4c..4365b5d38 100644 --- a/app/src/main/res/values-nl-rNL/strings.xml +++ b/app/src/main/res/values-nl-rNL/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Patches konden niet worden geladen. Klik om de fout te bekijken Patches zijn niet gedownload. Patches - Naamloos + Naamloos Android 11-bug De app-installatietoestemming moet vooraf worden verleend om een bug in het Android 11-systeem te voorkomen die de gebruikerservaring negatief zal beïnvloeden. Geen internetverbinding beschikbaar diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index 5b6e55709..bde78bf82 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Nie można było załadować łatek. Kliknij, aby wyświetlić błąd Poprawki nie zostały pobrane. Łatki - Bez nazwy + Bez nazwy Błąd Androida 11 Zezwolenie na instalację aplikacji musi zostać udzielone z wyprzedzeniem, aby uniknąć błędu w systemie Android 11, który negatywnie wpłynie na doświadczenia użytkownika. Brak połączenia z internetem diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 466b76965..0ff8befa4 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Os patches não puderam ser carregados. Clique para ver o erro Os patches não foram baixados. Patches - Sem nome + Sem nome Bug do Android 11 A permissão de instalação do aplicativo deve ser concedida antecipadamente para evitar um bug no sistema Android 11 que afetará negativamente a experiência do usuário. Nenhuma conexão de internet disponível diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 86cc72e9c..b2b8209de 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Não foi possível carregar os patches. Clique para ver o erro Os patches não foram baixados. Patches - Sem nome + Sem nome Bug do Android 11 A permissão de instalação da aplicação deve ser concedida antecipadamente para evitar um bug no sistema Android 11 que afetará negativamente a experiência do utilizador. Nenhuma conexão de internet disponível diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml index 93119fe3d..ce0c9a332 100644 --- a/app/src/main/res/values-ro-rRO/strings.xml +++ b/app/src/main/res/values-ro-rRO/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Patch-urile nu au putut fi încărcate. Apasă pentru a vedea eroarea. Pachetele nu au fost descărcate. Patch-uri - Fără nume + Fără nume Eroare Android 11 Permisiunea de instalare a aplicației trebuie acordată în avans pentru a evita o eroare în sistemul Android 11 care va afecta negativ experiența utilizatorului. Nicio conexiune la internet disponibilă diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index a5fbd50f8..3e359d6fa 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Патчи не могут быть загружены. Нажмите, чтобы просмотреть ошибку Патчи не были загружены. Патчи - Без названия + Без названия Ошибка Android 11 Разрешение на установку приложения должно быть предоставлено заранее, чтобы избежать ошибки в системе Android 11, которая негативно скажется на пользовательском опыте. Нет доступного интернет-соединения diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index ac6c142e7..df92425a3 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Patche sa nepodarilo načítať. Kliknutím zobrazíte chybu. Záplaty neboli stiahnuté. Patche - Bez názvu + Bez názvu Chyba systému Android 11 Povolenie na inštaláciu aplikácií musí byť udelené vopred, aby sa predišlo chybe v systéme Android 11, ktorá negatívne ovplyvní používateľský zážitok. Nie je k dispozícii žiadne internetové pripojenie diff --git a/app/src/main/res/values-sl-rSI/strings.xml b/app/src/main/res/values-sl-rSI/strings.xml index dcb4272c2..9e9ce2818 100644 --- a/app/src/main/res/values-sl-rSI/strings.xml +++ b/app/src/main/res/values-sl-rSI/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Popravkov ni bilo mogoče naložiti. Kliknite za ogled napake. Popravki niso bili preneseni. Popravki - Nepoimenovano + Nepoimenovano Napaka v Androidu 11 Dovoljenje za namestitev aplikacije mora biti podeljeno vnaprej, da se izognete napaki v sistemu Android 11, ki bi negativno vplivala na uporabniško izkušnjo. Ni na voljo internetne povezave diff --git a/app/src/main/res/values-sq-rAL/strings.xml b/app/src/main/res/values-sq-rAL/strings.xml index cc8194615..7969acbd3 100644 --- a/app/src/main/res/values-sq-rAL/strings.xml +++ b/app/src/main/res/values-sq-rAL/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Patch-et nuk mund të ngarkoheshin. Kliko për të parë gabimin. Arnat nuk janë shkarkuar. Patch-et - Pa emër + Pa emër Gabim në Android 11 Leja për instalimin e aplikacionit duhet të jepet paraprakisht për të shmangur një gabim në sistemin Android 11 që do të ndikojë negativisht në përvojën e përdoruesit. Nuk ka lidhje interneti në dispozicion diff --git a/app/src/main/res/values-sr-rCS/strings.xml b/app/src/main/res/values-sr-rCS/strings.xml index bafffce31..c3ff93622 100644 --- a/app/src/main/res/values-sr-rCS/strings.xml +++ b/app/src/main/res/values-sr-rCS/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Zakrpe se nisu mogle učitati. Kliknite da vidite grešku Zakrpe nisu preuzete. Zakrpe - Neimenovano + Neimenovano Greška Androida 11 Dozvola za instalaciju aplikacije mora biti data unapred kako bi se izbegla greška u sistemu Android 11 koja će negativno uticati na korisničko iskustvo. Nema dostupne internet veze diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml index f0e811792..5a3f04114 100644 --- a/app/src/main/res/values-sr-rSP/strings.xml +++ b/app/src/main/res/values-sr-rSP/strings.xml @@ -62,7 +62,7 @@ Second \"item\" text" Закрпе нису могле да буду учитане. Кликните да бисте видели грешку. Закрпе нису преузете. Закрпе - Неименовано + Неименовано Грешка Android-а 11 Дозвола за инсталацију апликације мора бити додељена унапред да би се избегла грешка у систему Android 11 која ће негативно утицати на корисничко искуство. Нема доступне интернет везе diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index afba294de..92532ca2e 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Korrigeringar kunde inte läsas in. Klicka för att visa felet Patchar har inte laddats ned. Korrigeringar - Namnlös + Namnlös Android 11-fel Behörigheten för appinstallation måste beviljas i förväg för att undvika en fel i Android 11-systemet som negativt påverkar användarupplevelsen. Ingen internetanslutning är tillgänglig diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index c47f2e184..d837b5cdc 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" ไม่สามารถโหลดแพตช์ได้ คลิกเพื่อดูข้อผิดพลาด ยังไม่ได้ดาวน์โหลดแพตช์ แพตช์ - ไม่มีชื่อ + ไม่มีชื่อ ข้อผิดพลาด Android 11 ต้องได้รับอนุญาตการติดตั้งแอปก่อนเวลาเพื่อหลีกเลี่ยงข้อผิดพลาดในระบบ Android 11 ซึ่งจะส่งผลเสียต่อประสบการณ์ผู้ใช้ ไม่มีการเชื่อมต่ออินเทอร์เน็ต diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index e4cb878da..a80a94802 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Yamalar yüklenemedi. Hatayı görmek için tıklayın Yamalar indirilmedi. Yamalar - Adsız + Adsız Android 11 hatası Uygulama yükleme izni, Android 11 sistemindeki kullanıcı deneyimini olumsuz etkileyecek bir hatayı önlemek için önceden verilmelidir. İnternet bağlantısı yok diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index 4a40c465c..cf733f6b6 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Не вдалося завантажити патчі. Натисніть, щоб переглянути помилку Патчі не завантажено. Патчі - Без назви + Без назви Помилка Android 11 Дозвіл на встановлення програми має бути наданий заздалегідь, щоб уникнути помилки в системі Android 11, яка негативно вплине на взаємодію з користувачем. Немає підключення до Інтернету diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml index a86fb1205..6b9d4f857 100644 --- a/app/src/main/res/values-vi-rVN/strings.xml +++ b/app/src/main/res/values-vi-rVN/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" Không thể tải các bản vá. Nhấn để xem lỗi Các bản vá chưa được tải xuống. Các bản vá - Chưa đặt tên + Chưa đặt tên Lỗi Android 11 Phải cấp quyền cài đặt ứng dụng trước để tránh lỗi trong hệ thống Android 11, vốn sẽ ảnh hưởng tiêu cực đến trải nghiệm người dùng. Không có kết nối internet khả dụng diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 355809b06..69a82c477 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" 无法加载补丁。点击查看错误。 补丁尚未下载。 补丁 - 未命名 + 未命名 Android 11 错误 必须提前授予应用安装权限,以避免 Android 11 系统中的一个错误,该错误将对用户体验产生负面影响。 无可用互联网连接 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 5872f46bb..7e4438390 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -59,7 +59,7 @@ Second \"item\" text" 無法載入修補程式。按一下以檢視錯誤 修補程式尚未下載。 修補程式 - 未命名 + 未命名 Android 11 錯誤 必須提前授予應用程式安裝權限,以避免 Android 11 系統中的錯誤對使用者體驗產生負面影響。 無可用的網際網路連線 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 824f74d27..ed425666d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,6 +26,7 @@ Second \"item\" text" Configure automatic updates to keep ReVanced Manager and patches up to date Patches and downloaders could not be downloaded during setup. Tap update to download them. + Patches or downloaders have updates, but were not downloaded because the network is metered. Tap update to download them. ReVanced Manager will connect to %s in order to download initial versions if your device is connected to the internet. Retry @@ -76,13 +77,11 @@ Second \"item\" text" Patches could not be loaded. Click to view the error Patches has not been downloaded. Patches - Unnamed + Unnamed Android 11 bug The app installation permission must be granted ahead of time to avoid a bug in the Android 11 system that will negatively affect the user experience. - No internet connection available - Any available version Select source Auto @@ -97,7 +96,6 @@ Second \"item\" text" Selection of patches has been changed No patches selected - Your device is not connected to the internet. Downloading will fail later. You are currently on a metered connection. Data charges from your service provider may apply. Select APK source @@ -118,7 +116,7 @@ Second \"item\" text" Do you want ReVanced Manager to periodically check for updates for the following components? ReVanced Manager ReVanced Patches - ReVanced Manager: Downloaders + APK Downloaders These settings can be changed later. ReVanced Manager will connect to %s in order to download initial versions if your device is connected to the internet. @@ -323,6 +321,7 @@ You will not be able to update the previously installed apps from this source.%1$dMB (Normal) - %2$dMB (Large) Force download all patches Reset patches + Reset downloaders Reset onboarding Show the onboarding screen on next app launch Reset announcement read