diff --git a/daemon/src/main/kotlin/org/matrix/vector/daemon/data/ConfigCache.kt b/daemon/src/main/kotlin/org/matrix/vector/daemon/data/ConfigCache.kt index 43250f2e9..0d27fa1d9 100644 --- a/daemon/src/main/kotlin/org/matrix/vector/daemon/data/ConfigCache.kt +++ b/daemon/src/main/kotlin/org/matrix/vector/daemon/data/ConfigCache.kt @@ -270,6 +270,15 @@ object ConfigCache { } val newScopes = mutableMapOf>() + + // A module can reach the same process by more than one route: self rows in two users each + // propagate into the other's, and the scope derived below can name a process a row named as + // well. Twice in the list is twice loaded, so every insertion goes through here. + fun addToScope(processName: String, uid: Int, module: Module) { + val modules = newScopes.getOrPut(ProcessScope(processName, uid)) { mutableListOf() } + if (modules.none { it === module }) modules.add(module) + } + ModuleDatabase.enabledScopeRows().forEach { scopeRow -> val appPkg = scopeRow.appPackage val modPkg = scopeRow.modulePackage @@ -278,7 +287,7 @@ object ConfigCache { val module = newModules[modPkg] ?: return@forEach if (appPkg == "system") { - newScopes.getOrPut(ProcessScope("system_server", 1000)) { mutableListOf() }.add(module) + addToScope("system_server", 1000, module) return@forEach } @@ -291,22 +300,40 @@ object ConfigCache { val appUid = pkgInfo.applicationInfo!!.uid for (processName in processNames) { - val processScope = ProcessScope(processName, appUid) - newScopes.getOrPut(processScope) { mutableListOf() }.add(module) + addToScope(processName, appUid, module) if (modPkg == appPkg) { val appId = appUid % PER_USER_RANGE userManager?.getRealUsers()?.forEach { user -> val moduleUid = user.id * PER_USER_RANGE + appId - if (moduleUid != appUid) { - val moduleSelf = ProcessScope(processName, moduleUid) - newScopes.getOrPut(moduleSelf) { mutableListOf() }.add(module) - } + if (moduleUid != appUid) addToScope(processName, moduleUid, module) } } } } + // A legacy module reports being active by hooking a method in its own app, so it has to be + // in its own scope before it can say anything at all. The manager used to add that row on + // every save and hide it again on read; #796 dropped both halves, and every legacy module + // has reported itself inactive since (#816). + // + // Derived here rather than stored, so a configuration written by those builds needs no + // repair and nothing that replaces the scope table can drop it again. Legacy is the + // loader's own verdict, so a module built against API 101 keeps its own process to itself. + newModules.values + .filter { it.file?.legacy == true } + .forEach { module -> + userManager?.getRealUsers()?.forEach { user -> + val pkgInfo = + packageManager?.getPackageInfoWithComponents( + module.packageName, MATCH_ALL_FLAGS, user.id) ?: return@forEach + val moduleUid = pkgInfo.applicationInfo?.uid ?: return@forEach + pkgInfo.fetchProcesses().forEach { processName -> + addToScope(processName, moduleUid, module) + } + } + } + // --- ATOMIC STATE SWAP --- // // Against the *current* state, not against the copy taken at the top of this function. A diff --git a/manager/src/main/kotlin/org/matrix/vector/manager/data/model/AppInfo.kt b/manager/src/main/kotlin/org/matrix/vector/manager/data/model/AppInfo.kt index 658f12506..5ccb3e50b 100644 --- a/manager/src/main/kotlin/org/matrix/vector/manager/data/model/AppInfo.kt +++ b/manager/src/main/kotlin/org/matrix/vector/manager/data/model/AppInfo.kt @@ -10,6 +10,14 @@ data class AppInfo( val isSystemApp: Boolean, val isGame: Boolean, val isSelectedInScope: Boolean, + /** + * In the scope without anyone having put it there, and not removable. + * + * Nothing in the scope table says so — the daemon derives this target while it rebuilds its + * configuration — so it is stamped on the row by the screen that knows the rule, exactly as + * [isSelectedInScope] and [isRecommended] are. + */ + val isImplicitInScope: Boolean = false, val isRecommended: Boolean, /** When the package was last installed or updated, for the "recently updated" sort. */ val lastUpdateTime: Long, diff --git a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeScreen.kt b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeScreen.kt index cb9037294..1bcb1f4f4 100644 --- a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeScreen.kt +++ b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeScreen.kt @@ -427,16 +427,26 @@ fun ScopeScreen( items(apps, key = { "${it.packageName}:${it.userId}" }) { app -> AppRow( app = app, - enabled = !state.recommended.staticScope, + enabled = !state.recommended.staticScope && !app.isImplicitInScope, origin = when { + app.isImplicitInScope -> ScopeOrigin.Derived state.recommended.staticScope && app.isRecommended -> ScopeOrigin.Locked app.isRecommended -> ScopeOrigin.Requested else -> ScopeOrigin.Chosen }, - sharedNote = - app.packageName == SYSTEM_FRAMEWORK_PACKAGE && state.multipleUsers, + // The framework's note only on a device that has more than one user: + // someone editing a work profile module's scope has no other way to know + // that this target is not scoped to their profile, but on a single-user + // phone it is a sentence about a distinction that does not exist. + note = + when { + app.isImplicitInScope -> R.string.scope_self_hook + app.packageName == SYSTEM_FRAMEWORK_PACKAGE && + state.multipleUsers -> R.string.scope_framework_shared + else -> null + }, onToggle = { checked -> haptics.performHapticFeedback( if (checked) HapticFeedbackType.ToggleOn @@ -808,6 +818,13 @@ private enum class ScopeOrigin { Requested, /** Nothing asked for it; it is in the scope because someone ticked it. */ Chosen, + /** + * The framework put it there, and no row in the scope table records it. + * + * A legacy module's own app: the daemon derives that target every time it rebuilds its + * configuration, so the tick is neither the user's nor the module's to give. + */ + Derived, } @Composable @@ -818,6 +835,9 @@ private fun ScopeOrigin.color(): Color = ScopeOrigin.Locked -> MaterialTheme.colorScheme.outline ScopeOrigin.Requested -> MaterialTheme.colorScheme.primary ScopeOrigin.Chosen -> Color.Transparent + // The same outline as Locked, and for the same reason: it is a tick nobody on this screen + // owns. The caption below it says which of the two kinds of "not yours" this one is. + ScopeOrigin.Derived -> MaterialTheme.colorScheme.outline } private fun ScopeOrigin.labelRes(): Int = @@ -825,6 +845,7 @@ private fun ScopeOrigin.labelRes(): Int = ScopeOrigin.Locked -> R.string.scope_origin_locked ScopeOrigin.Requested -> R.string.scope_recommended ScopeOrigin.Chosen -> R.string.scope_origin_chosen + ScopeOrigin.Derived -> R.string.scope_origin_derived } @Composable @@ -832,7 +853,14 @@ private fun AppRow( app: AppInfo, enabled: Boolean, origin: ScopeOrigin, - sharedNote: Boolean, + /** + * A sentence under the package name, for a row whose behaviour a label cannot carry. + * + * One slot rather than one flag per case: the two rows that have something to explain — the + * framework, and a legacy module's own app — are never the same row, and a boolean apiece + * would grow with every one that follows. + */ + note: Int?, onToggle: (Boolean) -> Unit, onAction: (PackageActionResult) -> Unit, ) { @@ -879,13 +907,12 @@ private fun AppRow( color = ring, ) } - // The one row on this screen that is not an app, and the one row offered - // identically to every user. Someone editing a work profile module's scope has no - // other way to know that this target is not scoped to their profile — but on a - // single-user device that is a sentence about a distinction that does not exist. - if (sharedNote) { + // Why this row does not behave like the rest: the framework is one process shared + // by every user, and a legacy module's own app is in the scope without anyone + // having put it there. Both are things a checkbox cannot say. + if (note != null) { Text( - text = stringResource(R.string.scope_framework_shared), + text = stringResource(note), style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, ) diff --git a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeViewModel.kt b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeViewModel.kt index ec4b87efc..a96fdb1de 100644 --- a/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeViewModel.kt +++ b/manager/src/main/kotlin/org/matrix/vector/manager/ui/screens/modules/ScopeViewModel.kt @@ -58,6 +58,16 @@ data class ScopeUiState( * about a distinction that does not exist, so it is not shown. */ val multipleUsers: Boolean = false, + /** + * Whether the module is loaded into its own process whatever the scope table says. + * + * A legacy module reports being active by hooking a method in its own app, so it has to be in + * its own scope before it can say anything at all, and the daemon derives that one target + * rather than storing it. Nothing comes back from `getModuleScope` to say so — so without + * this, the one row the module certainly hooks is the one row shown unticked, and with the + * module filter at its default it is not shown at all. + */ + val selfHooked: Boolean = false, ) class ScopeViewModel( @@ -225,6 +235,14 @@ class ScopeViewModel( // the other few hundred apps beneath uncheckable checkboxes offered a choice that // does not exist. This one stays absolute: there is no choice to preserve. val locked = view.state.recommended.staticScope + // The row the daemon hooks whether or not the table names it. Matched on the user + // as well as the package: the same module in a work profile is another copy with + // its own row, and only the copy this screen is editing is the one being loaded + // into the process in front of it. + fun implicit(app: AppInfo) = + view.state.selfHooked && + app.packageName == modulePackageName && + app.userId == userId filters.apps .asSequence() .filter { app -> !locked || app.packageName in recommended } @@ -236,7 +254,12 @@ class ScopeViewModel( // An app already in the scope is never filtered away. Otherwise a default // filter can hide a target the user deliberately chose, and the list then // disagrees with what the module is actually hooking. - val chosen = ScopeTarget(app.packageName, app.userId) in filters.draft + // Derived counts as chosen throughout, so the module's own row survives + // every filter — including the module filter, which is off by default and + // would otherwise hide the row this whole exemption exists to show. + val chosen = + implicit(app) || + ScopeTarget(app.packageName, app.userId) in filters.draft if (filters.recommendedOnly) { // Answers one question — what does this module want, and what have I // given it — and the other filters have no say in it. Chrome is a @@ -262,7 +285,9 @@ class ScopeViewModel( .map { app -> app.copy( isSelectedInScope = - ScopeTarget(app.packageName, app.userId) in filters.draft, + implicit(app) || + ScopeTarget(app.packageName, app.userId) in filters.draft, + isImplicitInScope = implicit(app), isRecommended = app.packageName in recommended, ) } @@ -297,8 +322,14 @@ class ScopeViewModel( // cannot be found by scrolling, so it has to lead the group it is being // picked from — and once it is in the scope it is a member like any other, // with no claim to sit above targets that are already in force. + // A derived row is in force by definition: it is not waiting on an apply, + // and grouping it with the newly ticked would promise a write that will + // never happen. val (inForce, newlyTicked) = - chosen.partition { ScopeTarget(it.packageName, it.userId) in filters.saved } + chosen.partition { + it.isImplicitInScope || + ScopeTarget(it.packageName, it.userId) in filters.saved + } inForce + frameworkFirst(newlyTicked) + frameworkFirst(rest) } } @@ -422,13 +453,15 @@ class ScopeViewModel( } .getOrNull() } - val recommended = + // One inspection, two answers: what the module asks to hook, and which generation of + // module it is. Both come out of the same pass over the APK, and opening it is the + // expensive part. + val manifest = info?.let { - withContext(Dispatchers.IO) { - val manifest = ModuleDetection.inspect(it, packageManager) - RecommendedScope(manifest.scope, manifest.staticScope) - } - } ?: RecommendedScope.NONE + withContext(Dispatchers.IO) { ModuleDetection.inspect(it, packageManager) } + } + val recommended = + manifest?.let { RecommendedScope(it.scope, it.staticScope) } ?: RecommendedScope.NONE _uiState.value = ScopeUiState( @@ -440,6 +473,11 @@ class ScopeViewModel( recommended = recommended, loading = false, multipleUsers = userCount > 1, + // The manager's own reading of the APK, not the daemon's. The daemon settles + // this while it loads the module and never tells anyone — and it only holds an + // answer for a module that is enabled, which is precisely not the state a + // module is in while its scope is being chosen for the first time. + selfHooked = manifest?.isLegacy == true, ) } } @@ -468,21 +506,33 @@ class ScopeViewModel( /** Local only. Nothing reaches the daemon until [apply]. */ fun toggle(app: AppInfo, selected: Boolean) { + // A derived row is not the scope table's to give or to take away. Writing a row of our own + // for it would neither add the target — it is already there — nor let it be removed, and + // unticking it would draw an empty box beside a process the module is still loaded into. + if (app.isImplicitInScope) return val target = ScopeTarget(app.packageName, app.userId) draftScope.value = if (selected) draftScope.value + target else draftScope.value - target } + // Both skip the derived row for the reason [toggle] gives: it is shown among the visible rows + // but it is not one of the ones being written, and either of these sweeping it up would report + // a change to a row whose tick nothing here decides. fun selectAllVisible() { draftScope.value = draftScope.value + - filteredApps.value.map { ScopeTarget(it.packageName, it.userId) } + filteredApps.value + .filterNot { it.isImplicitInScope } + .map { ScopeTarget(it.packageName, it.userId) } } fun clearAllVisible() { draftScope.value = draftScope.value - - filteredApps.value.map { ScopeTarget(it.packageName, it.userId) }.toSet() + filteredApps.value + .filterNot { it.isImplicitInScope } + .map { ScopeTarget(it.packageName, it.userId) } + .toSet() } /** Replace the draft with exactly what the module asked for. */ diff --git a/manager/src/main/res/values-ar/strings.xml b/manager/src/main/res/values-ar/strings.xml index e261d27fa..dc88b28c0 100644 --- a/manager/src/main/res/values-ar/strings.xml +++ b/manager/src/main/res/values-ar/strings.xml @@ -256,6 +256,7 @@ يربط إطار النظام ثابت من الوحدة اختيارك + دائمًا في النطاق إلغاء التحديد لم يُحدَّد شيء @@ -332,6 +333,7 @@ لن تُحتسب قديمة بعد الآن، لا هنا ولا في قائمة الوحدات خيارات عملية واحدة يتشاركها جميع المستخدمين، لذا يؤثر هذا في الجهاز كله + تُحمَّل هذه الوحدة داخل تطبيقها الخاص، وبهذا تُبلغ أنها فعّالة لم يُخرج برنامج التثبيت أي شيء. نهائي تنقيح diff --git a/manager/src/main/res/values-de/strings.xml b/manager/src/main/res/values-de/strings.xml index 12834592e..7a4cc773a 100644 --- a/manager/src/main/res/values-de/strings.xml +++ b/manager/src/main/res/values-de/strings.xml @@ -220,6 +220,7 @@ Hookt das System-Framework Vom Modul festgelegt Deine Wahl + Immer im Geltungsbereich Auswahl aufheben %1$d ausgewählt @@ -288,6 +289,7 @@ Es gilt hier und in der Modulliste nicht mehr als veraltet Optionen Ein Prozess, den sich alle Benutzer teilen — das betrifft das ganze Gerät + Dieses Modul wird in seine eigene App geladen — so meldet es, dass es aktiv ist Das Installationsprogramm hat nichts ausgegeben. Release Debug diff --git a/manager/src/main/res/values-es/strings.xml b/manager/src/main/res/values-es/strings.xml index 81b3fc94c..5e673f9d4 100644 --- a/manager/src/main/res/values-es/strings.xml +++ b/manager/src/main/res/values-es/strings.xml @@ -220,6 +220,7 @@ Hookea el framework del sistema Fijada por el módulo Tu elección + Siempre en el ámbito Borrar la selección %1$d seleccionado @@ -288,6 +289,7 @@ Deja de contar como desactualizado, aquí y en la lista de módulos Opciones Un único proceso compartido por todos los usuarios: afecta a todo el dispositivo + Este módulo se carga en su propia app: así es como informa de que está activo El instalador no produjo ninguna salida. Estable Depuración diff --git a/manager/src/main/res/values-fa/strings.xml b/manager/src/main/res/values-fa/strings.xml index 821c9081b..975a2826a 100644 --- a/manager/src/main/res/values-fa/strings.xml +++ b/manager/src/main/res/values-fa/strings.xml @@ -220,6 +220,7 @@ به چارچوب سامانه قلاب می‌زند ثابت‌شده از سوی ماژول گزینش شما + همیشه در دامنه برداشتن گزینش %1$d گزیده @@ -288,6 +289,7 @@ دیگر نه اینجا و نه در فهرست ماژول‌ها قدیمی شمرده نمی‌شود گزینه‌ها یک فرایند که همهٔ کاربران در آن شریک‌اند، پس این کل دستگاه را تحت تأثیر می‌گذارد + این ماژول در برنامهٔ خودش بارگذاری می‌شود و به همین شکل فعال‌بودنش را گزارش می‌کند نصب‌کننده چیزی چاپ نکرد. نهایی اشکال‌زدایی diff --git a/manager/src/main/res/values-fr/strings.xml b/manager/src/main/res/values-fr/strings.xml index 8daed06c8..fff29af17 100644 --- a/manager/src/main/res/values-fr/strings.xml +++ b/manager/src/main/res/values-fr/strings.xml @@ -220,6 +220,7 @@ Hooke le framework système Fixé par le module Votre choix + Toujours dans la portée Effacer la sélection %1$d sélectionné @@ -288,6 +289,7 @@ Il ne comptera plus comme obsolète, ici comme dans la liste des modules Options Un seul processus partagé par tous les utilisateurs : cela concerne tout l\'appareil + Ce module est chargé dans sa propre application : c\'est ainsi qu\'il signale qu\'il est actif L\'installateur n\'a rien affiché. Version finale Débogage diff --git a/manager/src/main/res/values-in/strings.xml b/manager/src/main/res/values-in/strings.xml index 9cc0accb1..ecb125acb 100644 --- a/manager/src/main/res/values-in/strings.xml +++ b/manager/src/main/res/values-in/strings.xml @@ -215,6 +215,7 @@ Meng-hook framework sistem Ditetapkan oleh modul Pilihan Anda + Selalu dalam cakupan Batalkan pilihan %1$d dipilih @@ -281,6 +282,7 @@ Ia berhenti dihitung usang, di sini maupun di daftar modul Opsi Satu proses yang dipakai bersama semua pengguna, jadi ini memengaruhi seluruh perangkat + Modul ini dimuat ke dalam aplikasinya sendiri, begitulah cara ia melaporkan bahwa dirinya aktif Pemasang tidak mengeluarkan apa pun. Rilis Debug diff --git a/manager/src/main/res/values-it/strings.xml b/manager/src/main/res/values-it/strings.xml index e5695f94f..4fa109120 100644 --- a/manager/src/main/res/values-it/strings.xml +++ b/manager/src/main/res/values-it/strings.xml @@ -220,6 +220,7 @@ Aggancia il framework di sistema Fissata dal modulo Scelta tua + Sempre nell\'ambito Annulla la selezione %1$d selezionato @@ -288,6 +289,7 @@ Smette di contare come non aggiornato, qui e nell\'elenco dei moduli Opzioni Un solo processo condiviso da tutti gli utenti: riguarda l\'intero dispositivo + Questo modulo viene caricato nella sua stessa app: è così che segnala di essere attivo Il programma di installazione non ha prodotto output. Stabile Debug diff --git a/manager/src/main/res/values-iw/strings.xml b/manager/src/main/res/values-iw/strings.xml index 6be53a4e0..593dbf7d3 100644 --- a/manager/src/main/res/values-iw/strings.xml +++ b/manager/src/main/res/values-iw/strings.xml @@ -242,6 +242,7 @@ מתחבר למסגרת המערכת נקבע על ידי המודול הבחירה שלכם + תמיד בתחום ניקוי הבחירה נבחר אחד @@ -314,6 +315,7 @@ הוא יפסיק להיחשב מיושן, כאן וברשימת המודולים אפשרויות תהליך אחד שכל המשתמשים חולקים, ולכן זה משפיע על כל המכשיר + המודול הזה נטען לתוך האפליקציה של עצמו, וכך הוא מדווח שהוא פעיל תוכנית ההתקנה לא הדפיסה דבר. יציבה ניפוי שגיאות diff --git a/manager/src/main/res/values-ja/strings.xml b/manager/src/main/res/values-ja/strings.xml index 509905424..d7a0322f2 100644 --- a/manager/src/main/res/values-ja/strings.xml +++ b/manager/src/main/res/values-ja/strings.xml @@ -211,6 +211,7 @@ システムフレームワークをフック モジュールが固定 あなたの選択 + 常に適用範囲内 選択を解除 %1$d 個選択中 @@ -277,6 +278,7 @@ ここでもモジュール一覧でも、古いものとして数えられなくなります オプション すべてのユーザーが共有する 1 つのプロセスなので、端末全体に影響します + このモジュールは自分自身のアプリに読み込まれ、そうやって有効であることを伝えます インストーラーは何も出力しませんでした。 正式版 デバッグ版 diff --git a/manager/src/main/res/values-ko/strings.xml b/manager/src/main/res/values-ko/strings.xml index fc068ce4b..c08305a08 100644 --- a/manager/src/main/res/values-ko/strings.xml +++ b/manager/src/main/res/values-ko/strings.xml @@ -211,6 +211,7 @@ 시스템 프레임워크를 후킹함 모듈이 고정함 직접 선택함 + 항상 적용 범위에 포함 선택 해제 %1$d개 선택됨 @@ -277,6 +278,7 @@ 여기서도 모듈 목록에서도 더 이상 오래된 것으로 세지 않습니다 옵션 모든 사용자가 함께 쓰는 하나의 프로세스라, 기기 전체에 영향을 줍니다 + 이 모듈은 자기 앱에 로드되며, 그렇게 해서 활성 상태임을 알립니다 설치 프로그램이 아무것도 출력하지 않았습니다. 정식 디버그 diff --git a/manager/src/main/res/values-pl/strings.xml b/manager/src/main/res/values-pl/strings.xml index dbe11afc9..773ef7d65 100644 --- a/manager/src/main/res/values-pl/strings.xml +++ b/manager/src/main/res/values-pl/strings.xml @@ -238,6 +238,7 @@ Podpina się pod szkielet systemu Ustalone przez moduł Twój wybór + Zawsze w zakresie Wyczyść zaznaczenie Zaznaczono %1$d @@ -310,6 +311,7 @@ Przestaje liczyć się jako nieaktualny, tutaj i na liście modułów Opcje Jeden proces wspólny dla wszystkich użytkowników, więc dotyczy to całego urządzenia + Ten moduł jest ładowany do własnej aplikacji i w ten sposób zgłasza, że działa Instalator nie wypisał nic. Stabilna Debug diff --git a/manager/src/main/res/values-pt-rBR/strings.xml b/manager/src/main/res/values-pt-rBR/strings.xml index e3395cc84..8f7836c0f 100644 --- a/manager/src/main/res/values-pt-rBR/strings.xml +++ b/manager/src/main/res/values-pt-rBR/strings.xml @@ -220,6 +220,7 @@ Dá hook no framework do sistema Fixado pelo módulo Escolha sua + Sempre no escopo Limpar a seleção %1$d selecionado @@ -288,6 +289,7 @@ Ele deixa de contar como desatualizado, aqui e na lista de módulos Opções Um único processo compartilhado por todos os usuários: isso afeta o aparelho inteiro + Este módulo é carregado no próprio app: é assim que ele informa que está ativo O instalador não produziu nenhuma saída. Estável Depuração diff --git a/manager/src/main/res/values-ru/strings.xml b/manager/src/main/res/values-ru/strings.xml index d6927c64b..665791f66 100644 --- a/manager/src/main/res/values-ru/strings.xml +++ b/manager/src/main/res/values-ru/strings.xml @@ -220,6 +220,7 @@ Перехватывает системный фреймворк Задано модулем Ваш выбор + Всегда в области действия Снять выделение Выбран %1$d @@ -291,6 +292,7 @@ Он перестанет считаться устаревшим и здесь, и в списке модулей Параметры Один процесс на всех пользователей — это затрагивает всё устройство + Модуль загружается в собственное приложение — так он и сообщает, что активен Установщик ничего не вывел. Стабильная Отладочная diff --git a/manager/src/main/res/values-tr/strings.xml b/manager/src/main/res/values-tr/strings.xml index dec883c10..1cdb259dd 100644 --- a/manager/src/main/res/values-tr/strings.xml +++ b/manager/src/main/res/values-tr/strings.xml @@ -220,6 +220,7 @@ Sistem çatısına hook takar Modül tarafından sabitlendi Sizin seçiminiz + Her zaman kapsamda Seçimi temizle %1$d seçildi @@ -288,6 +289,7 @@ Ne burada ne de modül listesinde artık eski sayılır Seçenekler Tüm kullanıcıların paylaştığı tek bir süreç, dolayısıyla bu cihazın tamamını etkiler + Bu modül kendi uygulamasına yüklenir, etkin olduğunu böyle bildirir Kurulum programı hiçbir çıktı üretmedi. Kararlı Hata ayıklama diff --git a/manager/src/main/res/values-uk/strings.xml b/manager/src/main/res/values-uk/strings.xml index 41bd5667f..a098377c7 100644 --- a/manager/src/main/res/values-uk/strings.xml +++ b/manager/src/main/res/values-uk/strings.xml @@ -238,6 +238,7 @@ Перехоплює системний фреймворк Задано модулем Ваш вибір + Завжди в області дії Зняти вибір Вибрано %1$d @@ -310,6 +311,7 @@ Він перестане рахуватися застарілим — і тут, і в переліку модулів Параметри Один процес, спільний для всіх користувачів, тож це стосується всього пристрою + Цей модуль завантажується у власний застосунок — так він і повідомляє, що активний Встановлювач нічого не вивів. Стабільна Зневаджувальна diff --git a/manager/src/main/res/values-vi/strings.xml b/manager/src/main/res/values-vi/strings.xml index 272b3fa77..b13f1f601 100644 --- a/manager/src/main/res/values-vi/strings.xml +++ b/manager/src/main/res/values-vi/strings.xml @@ -211,6 +211,7 @@ Hook framework hệ thống Do mô-đun cố định Bạn chọn + Luôn trong phạm vi Bỏ chọn Đã chọn %1$d @@ -277,6 +278,7 @@ Nó thôi bị tính là cũ, ở đây và trong danh sách mô-đun Tuỳ chọn Một tiến trình dùng chung cho mọi người dùng, nên việc này ảnh hưởng toàn thiết bị + Mô-đun này được nạp vào chính ứng dụng của nó, đó là cách nó báo rằng mình đang hoạt động Trình cài đặt không xuất ra gì cả. Chính thức Gỡ lỗi diff --git a/manager/src/main/res/values-zh-rCN/strings.xml b/manager/src/main/res/values-zh-rCN/strings.xml index 91b77ad80..3ea61edc4 100644 --- a/manager/src/main/res/values-zh-rCN/strings.xml +++ b/manager/src/main/res/values-zh-rCN/strings.xml @@ -211,6 +211,7 @@ 挂钩系统框架 由模块固定 你的选择 + 始终在作用域内 取消选择 已选 %1$d 个 @@ -278,6 +279,7 @@ 在此处与模块列表中都不再计为过期 选项 所有用户共用的同一个进程,因此这会影响整台设备 + 此模块会被加载到自己的应用中,它正是以此报告自己已生效 安装程序没有任何输出。 正式版 调试版 diff --git a/manager/src/main/res/values-zh-rTW/strings.xml b/manager/src/main/res/values-zh-rTW/strings.xml index bd6cb415f..e47d1ceda 100644 --- a/manager/src/main/res/values-zh-rTW/strings.xml +++ b/manager/src/main/res/values-zh-rTW/strings.xml @@ -211,6 +211,7 @@ 掛鉤系統框架 由模組固定 你的選擇 + 永遠在範圍內 取消選取 已選取 %1$d 個 @@ -278,6 +279,7 @@ 在此處與模組列表中都不再計為過期 選項 所有使用者共用的同一個行程,因此這會影響整台裝置 + 此模組會載入自己的應用程式,它正是以此回報自己已生效 安裝程式沒有任何輸出。 正式版 偵錯版 diff --git a/manager/src/main/res/values/strings.xml b/manager/src/main/res/values/strings.xml index 7db5e3e12..4f79f3eda 100644 --- a/manager/src/main/res/values/strings.xml +++ b/manager/src/main/res/values/strings.xml @@ -313,6 +313,7 @@ Hooks the system framework Fixed by the module Your choice + Always in scope Clear the selection %1$d selected @@ -410,6 +411,7 @@ It stops counting as out of date, here and on the modules list Options One process shared by every user, so this affects the whole device + This module is loaded into its own app, which is how it reports that it is active The installer produced no output. Release Debug