diff --git a/app/src/main/assets/i18n/es.tsv b/app/src/main/assets/i18n/es.tsv index f82f722..11633ee 100644 --- a/app/src/main/assets/i18n/es.tsv +++ b/app/src/main/assets/i18n/es.tsv @@ -276,7 +276,7 @@ Don't keep mobile data running while connected to Wi-Fi. Saves battery. No manti Done. Listo. Done: Listo: Downlink Descarga -Download Descargas +Download Descargar Draw over other apps Mostrar sobre otras apps Draws everything with the GPU instead of the display's overlay layers (the same as the developer option). Android forgets it at a restart; TinyKit puts it back once Shizuku is running. Dibuja todo con la GPU en lugar de las capas de superposición de la pantalla (igual que la opción de desarrollador). Android lo olvida al reiniciar; TinyKit lo vuelve a poner cuando Shizuku se está ejecutando. Edit quick panel Editar panel rápido @@ -1325,3 +1325,21 @@ Set a screen lock in Android first. Primero configura un bloqueo de pantalla en Use phone lock screen Usar el bloqueo del teléfono Only the folder is locked by this: the apps in the work profile can still be opened from the launcher while they aren't hidden. If you forget it, the phone's lock screen opens the folder. Esto solo bloquea la carpeta: las apps del perfil de trabajo se pueden abrir desde el lanzador mientras no estén ocultas. Si la olvidas, el bloqueo del teléfono abre la carpeta. Nothing here yet. Tap Add apps. Aún no hay nada. Toca Añadir apps. + +Root / Kernel Root / Kernel +Screen Pantalla +Idle Inactividad +Cell Red móvil +Wakelock Wakelocks +Estimated battery capacity Capacidad estimada +Last learned battery capacity Última capacidad aprendida +Min learned battery capacity Capacidad mínima aprendida +%d service running %d servicio en ejecución +%d services running %d servicios en ejecución +times veces +Deep Profundo +trimmed and compacted memory memoria optimizada y compactada +deleted %s of cache se eliminaron %s de caché +started storage maintenance se inició el mantenimiento del almacenamiento +closed cached apps se cerraron aplicaciones en caché +%s memory free now %s de memoria libre ahora diff --git a/app/src/main/assets/i18n/ru.tsv b/app/src/main/assets/i18n/ru.tsv index 73a46a2..0130c4e 100644 --- a/app/src/main/assets/i18n/ru.tsv +++ b/app/src/main/assets/i18n/ru.tsv @@ -187,7 +187,7 @@ Charging info Информация о зарядке Check for updates Проверить обновления Check for updates on start Проверять обновления при запуске Check for updates? Проверять обновления? -Checked apps are disabled: they leave the launcher and stop running. Uncheck to bring one back. On this phone only disabling works to hide an app. Отмеченные приложения отключаются: они исчезают из лаунчера и прекращают работу. Снимите отметку, чтобы вернуть их. На этом телефоне скрыть приложение можно только отключением. +Checked apps are disabled: they leave the launcher and stop running. Uncheck to bring one back. On this phone only disabling works to hide an app. Отмеченные приложения отключаются (замораживаются): они исчезают из лаунчера и полностью перестают работать в фоне. Снимите отметку, чтобы вернуть их (разморозить). На этом устройстве скрытие работает через отключение пакета. Checked apps are exempt from battery optimization and background limits, so Android doesn't kill them or delay their notifications. Отмеченные приложения исключены из оптимизации батареи и фоновых ограничений, поэтому Android не будет закрывать их или задерживать уведомления. Checked apps are restricted like Settings > Battery > Restricted: no background work, alarms or wake-locks until you open them. Their notifications may arrive late, so leave messengers unchecked. Отмеченные приложения ограничиваются, как в Настройки > Батарея > Ограничено: никакой фоновой работы, будильников или вейклоков до их открытия. Их уведомления могут приходить с задержкой, поэтому не отмечайте мессенджеры. Checked apps can't read what you copy, even when they're open. Android already stops apps from reading it in the background. Don't check your keyboard, or pasting suggestions stop working. Отмеченные приложения не могут читать то, что вы копируете, даже когда они открыты. Android уже запрещает чтение в фоновом режиме. Не отмечайте клавиатуру, иначе перестанут работать подсказки при вставке. @@ -278,7 +278,7 @@ Don't keep mobile data running while connected to Wi-Fi. Saves battery. Не п Done. Готово. Done: Готово: Downlink Скорость загрузки -Download Загрузки +Download Скачать Draw over other apps Поверх других приложений Draws everything with the GPU instead of the display's overlay layers (the same as the developer option). Android forgets it at a restart; TinyKit puts it back once Shizuku is running. Отрисовывает всё с помощью GPU вместо слоёв наложения экрана (аналогично опции для разработчиков). Android сбрасывает эту настройку при перезагрузке; TinyKit восстанавливает её после запуска Shizuku. Edit quick panel Изменить панель быстрых настроек @@ -336,9 +336,9 @@ Hearing devices Слуховые аппараты Held Удерживалось HiCloud HiCloud Hidden app-ops Скрытые app-ops -Hide apps Скрыть приложения +Hide apps Скрытие приложений Hide apps from the launcher Скрыть приложения из лаунчера -Hide apps from the launcher by disabling them. Скрыть приложения из лаунчера, отключив их. +Hide apps from the launcher by disabling them. Скрытие из лаунчера (заморозка) неиспользуемых приложений. Hide dev options Скрыть параметры разработчика Hide developer options Скрыть параметры для разработчиков Hide developer options from apps. Скрывать параметры разработчика от приложений. @@ -965,7 +965,7 @@ Wi-Fi sign-in pages Страницы входа в сети Wi-Fi Work profiles and device setup Рабочие профили и настройка устройства Work setup Настройка рабочего профиля Xiaomi push services Службы push-уведомлений Xiaomi - is out (you have уже доступна (у вас + is out (you have уже доступен (у вас just now только что under a minute меньше минуты not installed не установлено @@ -1171,7 +1171,7 @@ Permissions by type Разрешения по типу Pick a permission, like location or camera, and see every app that asks for it and which ones have it. Выберите разрешение, например геолокацию или камеру, и посмотрите, какие приложения его запрашивают и у каких оно есть. Pick a permission to see which apps ask for it and which have it. Выберите разрешение, чтобы увидеть, какие приложения его запрашивают и у каких оно есть. of из -apps have it приложений имеют его +apps have it приложений используют Allowed Разрешено Not allowed Не разрешено All permissions Все разрешения @@ -1232,34 +1232,34 @@ Termux returned no result. Termux не вернул результата. Command copied. Paste it in Termux (hold, Paste) and press Enter. Команда скопирована. Вставьте её в Termux (удерживайте, «Вставить») и нажмите Enter. Start by itself Запускать самостоятельно After a restart, and when you open TinyKit, start Shizuku if that needs nobody (it has worked before, or the auth token is saved). После перезагрузки и при открытии TinyKit запускать Shizuku, если для этого никто не нужен (раньше уже получалось или сохранён токен авторизации). -Deep sleep Глубокий сон +Deep sleep Управление спящим режимом (Doze) Light Лёгкий Moderate Умеренный High Высокий Extreme Экстремальный Android is using it. Android его использует. Android isn't using it yet. Android пока его не использует. -Android's own timing. Собственные тайминги Android. -Doze 10 minutes after the screen goes off. Doesn't change day-to-day use. Doze через 10 минут после выключения экрана. Не меняет повседневное использование. -Doze after a minute, wakes every 2 hours. Notifications arrive a little later. Doze через минуту, просыпается каждые 2 часа. Уведомления приходят чуть позже. -Doze after 30 seconds, wakes every 4 hours. Background work is delayed. Doze через 30 секунд, просыпается каждые 4 часа. Фоновая работа откладывается. -Doze as soon as the screen is off, wakes every 6 hours. The most battery saved, the most delayed. Doze сразу после выключения экрана, просыпается каждые 6 часов. Максимум экономии и максимум задержек. -Couldn't change deep sleep. Is Shizuku running? Не удалось изменить глубокий сон. Shizuku запущен? -Deep sleep now Глубокий сон сейчас -Puts the phone into Doze right now, until you use it again. Turn the screen off to see it work. Переводит телефон в Doze прямо сейчас, пока вы им не воспользуетесь. Выключите экран, чтобы увидеть. -Done. Doze is on. Готово. Doze включён. +Android's own timing. Стандартные параметры Android. +Doze 10 minutes after the screen goes off. Doesn't change day-to-day use. Засыпание через 10 минут после выключения экрана. Не влияет на обычное использование. +Doze after a minute, wakes every 2 hours. Notifications arrive a little later. Сон через 1 минуту, просыпается каждые 2 часа. Уведомления приходят чуть позже. +Doze after 30 seconds, wakes every 4 hours. Background work is delayed. Сон через 30 секунд, просыпается каждые 4 часа. Фоновая работа откладывается. +Doze as soon as the screen is off, wakes every 6 hours. The most battery saved, the most delayed. Сон сразу при выключении экрана, просыпается каждые 6 часов. Максимум экономии и максимум задержек. +Couldn't change deep sleep. Is Shizuku running? Не удалось изменить спящий режим. Shizuku запущен? +Deep sleep now Перевести в спящий режим сейчас +Puts the phone into Doze right now, until you use it again. Turn the screen off to see it work. Немедленно переводит телефон в спящий режим (Doze). Режим активируется после выключения экрана. +Done. Doze is on. Готово. Режим сна включён. Wake up Разбудить -Takes the phone out of deep sleep. Выводит телефон из глубокого сна. -Apps that ignore deep sleep Приложения, игнорирующие глубокий сон +Takes the phone out of deep sleep. Выводит телефон из спящего режима. +Apps that ignore deep sleep Приложения, игнорирующие спящий режим Apps you keep alive (and the system's own) don't doze. Приложения, которые вы оставляете живыми (и системные), не засыпают. -Make the phone doze sooner after the screen goes off, and wake up less often, like Comatose. Pick a profile, or doze right now. Телефон засыпает раньше после выключения экрана и просыпается реже, как в Comatose. Выберите профиль или усыпите сейчас. +Make the phone doze sooner after the screen goes off, and wake up less often, like Comatose. Pick a profile, or doze right now. Управление спящим режимом Doze: ускоряет засыпание после выключения экрана и сокращает пробуждения для экономии батареи. Running services Запущенные службы Every service running now, with its app. Stop one, or block it for good. Все запущенные сейчас службы с их приложениями. Остановите службу или заблокируйте её насовсем. Tap a service to stop it, stop its app, or block it. Stopping can be undone by the app; blocking can't, until you unblock it. Нажмите на службу, чтобы остановить её, остановить приложение или заблокировать. Приложение может запустить остановленную службу снова; заблокированную — нет, пока вы не разблокируете. Search services Поиск служб Show system services Показывать системные службы -service running служба запущена -services running служб запущено +service running служба активна +services running активных служб Foreground Передний план Started Запущена Bound Привязана @@ -1328,3 +1328,31 @@ Set a screen lock in Android first. Сначала настройте блоки Use phone lock screen Использовать экран блокировки Only the folder is locked by this: the apps in the work profile can still be opened from the launcher while they aren't hidden. If you forget it, the phone's lock screen opens the folder. Этим блокируется только папка: приложения рабочего профиля можно открыть из лаунчера, пока они не скрыты. Если забудете пароль, папку откроет экран блокировки телефона. Nothing here yet. Tap Add apps. Пока ничего нет. Нажмите «Добавить приложения». + +ACTIVE Активен +INACTIVE Неактивен +IDLE Режим ожидания +IDLE_MAINTENANCE Обслуживание +IDLE_PENDING Ожидание сна +SENSING Датчики +LOCATING Локация +QUICK_DOZE_DELAY Задержка сна +WAITING_FOR_NETWORK Ожидание сети +OVERRIDE Принудительный режим +Wakelock Вейклоки +Screen Экран +Idle Режим ожидания +Cell Мобильная связь +Root / Kernel Root / Ядро +Estimated battery capacity Расчётная ёмкость +Last learned battery capacity Фактическая ёмкость (замер) +Min learned battery capacity Минимальная замеренная ёмкость +%d service running Активна %d служба +%d services running Активных служб: %d +times раз +Deep Глубокий +trimmed and compacted memory память оптимизирована и сжата +deleted %s of cache удалено %s кэша +started storage maintenance запущено обслуживание хранилища +closed cached apps закрыты фоновые приложения +%s memory free now сейчас свободно %s памяти diff --git a/app/src/main/java/io/github/order52/tinykit/Doze.java b/app/src/main/java/io/github/order52/tinykit/Doze.java index 500f25e..3247596 100644 --- a/app/src/main/java/io/github/order52/tinykit/Doze.java +++ b/app/src/main/java/io/github/order52/tinykit/Doze.java @@ -134,7 +134,7 @@ static Status read() { m = Pattern.compile("mLightState=(\\w+)").matcher(line); if (m.find()) light = m.group(1); } - status.state = deep.isEmpty() ? "" : Tr.t("Deep sleep") + ": " + deep + " · " + Tr.t("Light") + ": " + light; + status.state = deep.isEmpty() ? "" : Tr.t("Deep") + ": " + Tr.t(deep) + " · " + Tr.t("Light") + ": " + Tr.t(light); return status; } diff --git a/app/src/main/java/io/github/order52/tinykit/MainActivity.java b/app/src/main/java/io/github/order52/tinykit/MainActivity.java index bb1d905..6350154 100644 --- a/app/src/main/java/io/github/order52/tinykit/MainActivity.java +++ b/app/src/main/java/io/github/order52/tinykit/MainActivity.java @@ -1343,7 +1343,7 @@ private void hideKeyboard(View view) { private void addTile(Class service, String label, int icon, String about) { if (Build.VERSION.SDK_INT >= 33) { getSystemService(android.app.StatusBarManager.class).requestAddTileService( - new ComponentName(this, service), label, + new ComponentName(this, service), Tr.t(label), android.graphics.drawable.Icon.createWithResource(this, icon), getMainExecutor(), result -> { }); @@ -1886,7 +1886,9 @@ private void openServices() { || label(sv.pkg).toLowerCase(Locale.ROOT).contains(q)) shown.add(sv); } adapter.set(shown); - summary.setText(shown.size() + " " + Tr.t(shown.size() == 1 ? "service running" : "services running")); + String pattern = Tr.t(shown.size() == 1 ? "%d service running" : "%d services running"); + summary.setText(pattern.contains("%d") ? String.format(Locale.getDefault(), pattern, shown.size()) + : shown.size() + " " + pattern); }; search.addTextChangedListener(watcher(filter)); system.setOnCheckedChangeListener((v, on) -> filter.run()); @@ -4024,11 +4026,13 @@ private List phoneInfo() { String out = Shell.run("cat /sys/class/power_supply/*/cycle_count 2>/dev/null | head -1").out; if (out.matches("\\d+")) cycles = out; } - String stats = Shell.run("dumpsys batterystats | grep -m3 -iE " + String stats = Shell.run("dumpsys batterystats 2>/dev/null | grep -m3 -iE " + "'Estimated battery capacity|learned battery capacity'").out; for (String line : stats.split("\n")) { int colon = line.indexOf(':'); - if (colon > 0) info.add(new String[]{line.substring(0, colon).trim(), line.substring(colon + 1).trim()}); + if (colon > 0 && line.toLowerCase(Locale.ROOT).contains("battery capacity")) { + info.add(new String[]{line.substring(0, colon).trim(), line.substring(colon + 1).trim()}); + } } } info.add(new String[]{"Charge cycles", cycles != null ? cycles : "Not reported by this phone"}); @@ -5167,11 +5171,13 @@ private void openChargingInfo() { if (!TextUtils.isEmpty(tech)) info.add(new String[]{"Technology", tech}); } if (Shell.ready()) { - for (String line : Shell.run("dumpsys batterystats | grep -m3 -iE " + for (String line : Shell.run("dumpsys batterystats 2>/dev/null | grep -m3 -iE " + "'Estimated battery capacity|learned battery capacity'").out.split("\n")) { int colon = line.indexOf(':'); - if (colon > 0) info.add(new String[]{line.substring(0, colon).trim(), - line.substring(colon + 1).trim()}); + if (colon > 0 && line.toLowerCase(Locale.ROOT).contains("battery capacity")) { + info.add(new String[]{line.substring(0, colon).trim(), + line.substring(colon + 1).trim()}); + } } } return info; @@ -5260,7 +5266,7 @@ private List batteryUsage() { String token = who.substring(4).trim(); String p = uidPackage(token); pkg = p != null && installedHere(p) ? p : ""; - who = p != null ? uidTitle(p) : token; + who = p != null ? uidTitle(p) : ("0".equals(token) ? "Root / Kernel" : token); } else { who = who.replace('_', ' '); who = who.substring(0, 1).toUpperCase(Locale.ROOT) + who.substring(1); @@ -5342,17 +5348,38 @@ private void openWakeups() { "Nothing recorded since the last charge, or this phone hides it.", this::wakeups); } + private static String formatTimes(String countStr) { + int count = 0; + try { + count = Integer.parseInt(countStr); + } catch (NumberFormatException ignored) { + return countStr + " " + Tr.t("times"); + } + String lang = Locale.getDefault().getLanguage(); + if ("ru".equals(lang)) { + int c10 = Math.abs(count) % 10; + int c100 = Math.abs(count) % 100; + String word = (c10 >= 2 && c10 <= 4 && (c100 < 10 || c100 >= 20)) ? "раза" : "раз"; + return count + " " + word; + } + if ("es".equals(lang)) { + return count + " " + (count == 1 ? "vez" : "veces"); + } + return count + " " + (count == 1 ? "time" : "times"); + } + private List wakeups() { forgetProfiles(); List rows = new ArrayList<>(); String out = Shell.run("dumpsys batterystats --charged | sed -n '/All partial wake locks/,/^$/p'").out; - Pattern p = Pattern.compile("Wake lock (\\S+) (.+?): ([0-9hms ]+?) \\((\\d+) times\\)"); + Pattern p = Pattern.compile("Wake lock (\\S+) (.+?): ([0-9hms ]+?) \\((\\d+) times?\\)"); for (String line : out.split("\n")) { Matcher m = p.matcher(line.trim()); if (!m.find()) continue; String pkg = uidPackage(m.group(1)); - String who = pkg != null ? uidTitle(pkg) : m.group(1); - String detail = "Held " + m.group(3).trim() + " · " + m.group(4) + " times · " + m.group(2); + String who = pkg != null ? uidTitle(pkg) : ("0".equals(m.group(1)) ? "Root / Kernel" : m.group(1)); + String detail = Tr.t("Held ") + m.group(3).trim() + " · " + + formatTimes(m.group(4)) + " · " + m.group(2); rows.add(new String[]{who, detail, pkg != null && installedHere(pkg) ? pkg : ""}); } return rows; diff --git a/app/src/main/java/io/github/order52/tinykit/Ram.java b/app/src/main/java/io/github/order52/tinykit/Ram.java index 24f553a..0f7e434 100644 --- a/app/src/main/java/io/github/order52/tinykit/Ram.java +++ b/app/src/main/java/io/github/order52/tinykit/Ram.java @@ -68,6 +68,27 @@ static long available(Context c) { * 3. trims storage (TRIM) and, once a day, starts Android's idle maintenance. * Returns a short result for the user. Runs off the main thread. */ + private static String formatClosedApps(int closed) { + String lang = java.util.Locale.getDefault().getLanguage(); + if ("ru".equals(lang)) { + int c10 = Math.abs(closed) % 10; + int c100 = Math.abs(closed) % 100; + String word; + if (c10 == 1 && c100 != 11) { + word = "фоновое приложение"; + } else if (c10 >= 2 && c10 <= 4 && (c100 < 10 || c100 >= 20)) { + word = "фоновых приложения"; + } else { + word = "фоновых приложений"; + } + return "закрыто " + closed + " " + word; + } + if ("es".equals(lang)) { + return "se " + (closed == 1 ? "cerró 1 app en caché" : "cerraron " + closed + " apps en caché"); + } + return "closed " + closed + " cached app" + (closed == 1 ? "" : "s"); + } + static String free(Context c) { long beforeRam = available(c); long beforeStorage = freeStorage(); @@ -76,17 +97,21 @@ static String free(Context c) { if (Shell.ready()) { Shell.Result caches = Shell.run("pm trim-caches 999G"); long cleared = Math.max(0, freeStorage() - beforeStorage); - if (caches.ok() && cleared > 0) did.add("deleted " + Formatter.formatShortFileSize(c, cleared) + " of cache"); + if (caches.ok() && cleared > 0) { + String size = Formatter.formatShortFileSize(c, cleared); + String pattern = Tr.t("deleted %s of cache"); + did.add(pattern.contains("%s") ? String.format(pattern, size) : "deleted " + size + " of cache"); + } Shell.Result r = Shell.run(script()); String[] lines = r.out.split("\n"); try { closed = Math.max(0, Integer.parseInt(lines[lines.length - 1].trim())); } catch (NumberFormatException e) { - if (!r.ok()) return "Failed: " + r.out; + if (!r.ok()) return Tr.t("Failed: ") + r.out; } - if (closed > 0) did.add("closed " + closed + " cached app" + (closed == 1 ? "" : "s")); + if (closed > 0) did.add(formatClosedApps(closed)); Shell.run("sm fstrim"); - if (maintenance(c)) did.add("started storage maintenance"); + if (maintenance(c)) did.add(Tr.t("started storage maintenance")); } else if (!needsShizuku()) { ActivityManager am = c.getSystemService(ActivityManager.class); for (ApplicationInfo app : c.getPackageManager().getInstalledApplications(0)) { @@ -99,16 +124,18 @@ static String free(Context c) { } } SystemClock.sleep(1000); - did.add("closed cached apps"); + did.add(Tr.t("closed cached apps")); } else { - return "Start Shizuku first."; + return Tr.t("Start Shizuku first."); } long freed = Math.max(0, available(c) - beforeRam); - String memory = Formatter.formatShortFileSize(c, available(c)) + " memory free now" - + (freed >= 1024 * 1024 ? " (+" + Formatter.formatShortFileSize(c, freed) + ")" : "") + "."; - if (!Shell.ready()) return (did.isEmpty() ? "Done." : "Done: " + String.join(", ", did) + ".") + " " + memory; - did.add(0, "trimmed and compacted memory"); - return "Done: " + String.join(", ", did) + ". " + memory; + String avail = Formatter.formatShortFileSize(c, available(c)); + String plus = freed >= 1024 * 1024 ? " (+" + Formatter.formatShortFileSize(c, freed) + ")" : ""; + String memPattern = Tr.t("%s memory free now"); + String memory = (memPattern.contains("%s") ? String.format(memPattern, avail) : avail + " " + Tr.t("memory free now")) + plus + "."; + if (!Shell.ready()) return (did.isEmpty() ? Tr.t("Done.") : Tr.t("Done: ") + String.join(", ", did) + ".") + " " + memory; + did.add(0, Tr.t("trimmed and compacted memory")); + return Tr.t("Done: ") + String.join(", ", did) + ". " + memory; } /** Starts Android's idle maintenance (TRIM, compiling) in the system, at most once a day. */ @@ -130,8 +157,8 @@ static String clearCaches(Context c) { long before = freeStorage(); // Asking for far more free space than any phone has makes Android clear every cache. Shell.Result r = Shell.run("pm trim-caches 999G"); - if (!r.ok()) return "Failed: " + r.out; - return "Freed " + Formatter.formatShortFileSize(c, Math.max(0, freeStorage() - before)); + if (!r.ok()) return Tr.t("Failed: ") + r.out; + return Tr.t("Freed ") + Formatter.formatShortFileSize(c, Math.max(0, freeStorage() - before)); } private static long freeStorage() {