From 9e200d508c85e5c3cac58634eb389a5a170a5516 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 16 Jan 2026 23:27:27 +0530 Subject: [PATCH 001/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 3fc26021..fbf7044c 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -744,15 +744,15 @@ フリーズとは? 一時停止してアプリを邪魔することなく、バックグラウンドでアプリが動作しないようにすることで、電力を少し節約できます。あまり使用しないアプリに適しています。\n\nSNSなどの通信サービスは、フリーズを解除しない限り、緊急時に通知されないため、推奨されません。\n\nシステムアプリをフリーズするとシステムが不安定になる可能性があるため、フリーズしないことを強くおすすめします。注意して操作してください。警告しましたからね!\n\nInspired by Hail <3 アプリロックとロック画面セキュリティは本当に安全ですか? - Absolutely not. \n\nAny 3rd party application can not 100% interfere with regular device interactions and even the app lock is only an overlay above selected apps to prevent interacting with them. There are workarounds and it is not foolproof. \n\nSame goes with the screen locked security feature which detects someone trying to interact with the network tiles which for some reason are still accessible for anyone on Pixels. So if they try hard enough they might still be able to change them and especially if you have a flight mode QS tile added, this app can not prevent interactions with it. \n\nThese features are made just as experiments for light usage and would never recommend as strong security and privacy solutions. \n\nSecure alternatives:\n - App lock: Private Space and Secure folder on Pixels and Samsung\n - Preventing mobile networks access: Make sure your theft protection and offline/ power off find my device settings are on. You may look into Graphene OS as well. - Statusbar icons - You may notice that even after resetting the statusbar icons, Some icons such as device rotation, wired headphone icons may stay visible. This is due to how the statubar blacklist is implemented in Android and how your OEM may have customized them. \nYou may need further adjustments. \n\nAlso not all icon visibility options may work as they depend on the OEM implementations and availability. - Notification lighting does not work - It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. - Button remap does not work while display is off + 絶対に安全とは限りません。\n\nサードパーティ製アプリは、通常のデバイス操作を100%妨害することはできません。アプリロックでさえ、特定のアプリとのやり取りを阻止するためのオーバーレイにすぎません。回避策としては有効でありますが、万全ではありません。\n\nロック画面セキュリティ機能も同様です。この機能は、ネットワークタイルへの操作を試みる人物を検出しますが、何らかの理由でPixelでは誰でもアクセスできます。そのため、十分に試行錯誤すれば、タイルを変更できる可能性があります。特に、機内モードのクイック設定タイルを追加している場合は、このアプリでタイルとのやり取りを阻止することはできません。\n\nこれらの機能は、軽い使用のための実験として作成されており、強力なセキュリティおよびプライバシーソリューションとして推奨されていません。\n\n安全な代替手段:\n・アプリロック:PixelやSamsungにあるプライベートスペースやセキュアフォルダ\n・ロック画面セキュリティ:盗難防止機能とオフラインや電源オフ時のデバイスを探す設定がオンになっていることを確認してください。Graphene OSも検討してみてください。 + ステータスバーアイコン + ステータスバーアイコンをリセットした後でも、デバイスの自動回転やヘッドセットアイコンなど、一部のアイコンが表示されたままになる場合があります。これは、Androidでのステータスバーのブラックリストの実装方法と、OEMによるカスタマイズが原因です。\n更に調整が必要になる場合があります。\n\nまた、OEMの実装と可用性に依存するため、全てのアイコン表示オプションが機能するとは限りません。 + 通知ライトが機能しません + OEMによって動作が異なります。OneUIなど、一部のOEMでは常時表示ディスプレイの上にオーバーレイを許可していないため、通知ライトが表示されません。この場合、回避策として、アンビエントディスプレイを試してください。 + 画面がオフのときのボタンリマップが機能しません Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. - Flashlight brightness does not work - Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu + フラッシュライトの明るさ調整が機能しません + フラッシュライトの明るさ調整をハードウェアとソフトウェアでサポートしているデバイスは限られています。\n\n「Androidの最小バージョンは13 (SDK33) です。\nフラッシュライトの明るさ調整はHALバージョン3.8以降のみをサポートしているため、サポートされている中で最新のもの(例:Pixel 6/7、Samsung S23など)のみが対象です。」\npolodarb/Flashlight-Tiramisu What the hell is this app? Good question,\n\nI always wanted to extract the most out of my devices as I\'ve been a rooted user for ever since I got my first Project Treble device. And I\'ve been loving the Tasker app which is like the god when comes automation and utilizing every possible API and internal features of Android.\n\nSo I am not unrooted and back on stock Android beta experience and wanted to get the most out from what is possible with given privileges. Might as well share them. So with my beginner knowledge in Kotlin Jetpack and with the support of many research and assist tools and also the great community, I built an all-in-one app containing everything I wanted to be in my Android with given permissions. And here it is.\n\nFeature requests are welcome, I will consider and see if they are achievable with available permissions and my skills. Nowadays what is not possible. :)\n\nWhy not on Play Store?\nI don\'t wanna risk getting my Developer account banned due to the highly sensitive and internal permissions and APIs being used in the app. But with the way Android sideloading is headed, let\'s see what we have to do. I do understand the concerns of sideloaded apps being malicious.\nWhile we are at the topic, Checkout my other app AirSync if you are a mac + Android user. *shameless plug*\n\nEnjoy, Keep building! (っ◕‿◕)っ @@ -804,16 +804,16 @@ Travel Alarm active %1$s remaining (%2$d%%) Travel Progress - Shows real-time distance to destination + 目的地までの距離をリアルタイムで表示 Destination Nearby Prepare to get off Dismiss Destination set: %1$.4f, %2$.4f - Use Root - Instead of Shizuku - Root access not available. Please check your root manager. + Rootを使う + Shizukuの代わりに + Rootアクセスができません。Rootマネージャーを確認してください。 - Keyboard + キーボード Keys Customize layout and behavior Keyboard Height From 565a3850e910fde6ee9df8cdd539cdab6fcf75b3 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 08:01:40 +0530 Subject: [PATCH 002/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index fbf7044c..275c117f 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -270,12 +270,12 @@ クイック設定タイルでサイレントモードを切り替える スリープしないタイル クイック設定タイルで充電中にスリープするかを切り替える - Show Bluetooth devices - Display battery level of connected Bluetooth devices - Limit max devices - Adjust max devices visible in widget - Widget background - Show widget background + Bluetoothデバイスを表示 + 接続されているBluetoothデバイスのバッテリー残量を表示する + デバイスの上限 + ウィジェットに表示するデバイスの最大数を調整する + バックグラウンドウィジェット + バックグラウンドでウィジェットを表示する トリガーオートメーション トリガーされるアクションをスケジュールする From 429bbcae6e5351215fdef98bc3470fefdd378012 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 12:05:54 +0530 Subject: [PATCH 003/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 275c117f..d1fcdbb7 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -474,7 +474,7 @@ 有効 無効 オートメーションサービス - Automations Active + オートメーション動作中 Monitoring system events for your automations Sameera Wijerathna From ece1306f6a14854a34b374ad9547fb7675f5fce7 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 13:01:15 +0530 Subject: [PATCH 004/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 32 +++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index d1fcdbb7..616139f3 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -475,32 +475,32 @@ 無効 オートメーションサービス オートメーション動作中 - Monitoring system events for your automations + 自動化システムイベントを監視中です Sameera Wijerathna - The all-in-one toolbox for your Pixel and Androids - Hello from Essentials + あなたのPixelとAndroidのオールインワンツールボックス + Essentialsからこんにちは - System - Custom - App specific + システム + カスタム + アプリ固有 - Authentication failed - This is a beta version. Please report any bugs. - Long press an app in the grid to add a shortcut - App not found or uninstalled + 認証失敗 + これはベータ版です。バグがありましたらご報告ください。 + グリッド内のアプリを長押ししてショートカットを作成します + アプリが見つからないかアンインストールされています - App Updates - Notifications for new app updates - Update available - Unknown + アプリアップデート + 新しいアップデートの通知 + アップデートがあります + 不明 5G 4G 3G Shizuku (Rikka) Shizuku (TuoZi) - Search - Required to hard-lock the device when unauthorized network changes are attempted on lock screen. + 検索 + ロック画面で不正なネットワーク変更が試みられた場合、デバイスをハードロックする必要があります。 Authenticate to access settings Authenticate to enable this feature Authenticate to disable this feature From 0d7bfe5baa421f26f800608b556670101864b77f Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 14:17:20 +0530 Subject: [PATCH 005/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 60 +++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 616139f3..7a9ccaa5 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -501,34 +501,34 @@ Shizuku (TuoZi) 検索 ロック画面で不正なネットワーク変更が試みられた場合、デバイスをハードロックする必要があります。 - Authenticate to access settings - Authenticate to enable this feature - Authenticate to disable this feature - %1$s Settings - feature - settings - hide - show - visibility - Error loading apps: %1$s + 設定にアクセスするための認証 + この機能を有効化するための認証 + この機能を無効化するための認証 + %1$s設定 + 機能 + 設定 + 非表示 + 表示 + 可視性 + アプリ読み込みエラー:%1$s - vibration - touch - feel + バイブレーション + タッチ + 感じる - network - visibility - auto - hide + ネットワーク + 可視性 + 自動 + 非表示 - restore - default - icon + 復元 + デフォルト + アイコン - keyboard + キーボード height padding haptic @@ -690,13 +690,13 @@ You are up to date This is a pre-release version and might be unstable. Release Notes v%1$s - View on GitHub - Download APK + GitHubを見る + APKをダウンロード - None - Subtle - Double - Click + なし + 微妙 + ダブル + クリック ティック オフにする @@ -729,7 +729,7 @@ メール メールを送る メールアプリがありません - Step %1$d Image + ステップ%1$dの画像 アクセシビリティ、通知、他のアプリの上に重ねて表示権限 アクセシビリティ、通知の読み取り、オーバーレイなどの機密性の高い権限を付与しようとすると、このアクセス拒否メッセージが表示されることがあります。付与するには以下の手順を確認してください。 @@ -753,10 +753,10 @@ Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. フラッシュライトの明るさ調整が機能しません フラッシュライトの明るさ調整をハードウェアとソフトウェアでサポートしているデバイスは限られています。\n\n「Androidの最小バージョンは13 (SDK33) です。\nフラッシュライトの明るさ調整はHALバージョン3.8以降のみをサポートしているため、サポートされている中で最新のもの(例:Pixel 6/7、Samsung S23など)のみが対象です。」\npolodarb/Flashlight-Tiramisu - What the hell is this app? + このアプリは一体何ですか? Good question,\n\nI always wanted to extract the most out of my devices as I\'ve been a rooted user for ever since I got my first Project Treble device. And I\'ve been loving the Tasker app which is like the god when comes automation and utilizing every possible API and internal features of Android.\n\nSo I am not unrooted and back on stock Android beta experience and wanted to get the most out from what is possible with given privileges. Might as well share them. So with my beginner knowledge in Kotlin Jetpack and with the support of many research and assist tools and also the great community, I built an all-in-one app containing everything I wanted to be in my Android with given permissions. And here it is.\n\nFeature requests are welcome, I will consider and see if they are achievable with available permissions and my skills. Nowadays what is not possible. :)\n\nWhy not on Play Store?\nI don\'t wanna risk getting my Developer account banned due to the highly sensitive and internal permissions and APIs being used in the app. But with the way Android sideloading is headed, let\'s see what we have to do. I do understand the concerns of sideloaded apps being malicious.\nWhile we are at the topic, Checkout my other app AirSync if you are a mac + Android user. *shameless plug*\n\nEnjoy, Keep building! (っ◕‿◕)っ - Report Bug + バグを報告 Bug report copied to clipboard Bug report Share logs From c069da2cc1328cb99754761442edf5b718cc5fd1 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 16:02:54 +0530 Subject: [PATCH 006/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 21fea74e..2f32cf6d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -27,7 +27,7 @@ Mais également, la fenêtre d\'authentification biométrique ne vous laisse utiliser que des méthodes d\'authentification FORTES. Le déverrouillage par reconnaissance faciale est considéré comme FAIBLE pour les appareils comme les Pixel 7 qui ne pourront utiliser que les autres méthodes FORTES disponibles comme l\'authentification par empreinte digitale ou code PIN. Activer la réattribution de boutons - Utiliser Shizuku + Use Shizuku Fonctionne avec l\'écran éteint (Recommandé) Shizuku n\'est pas actif %1$s Détecté @@ -379,7 +379,7 @@ Shizuku Requis pour les commandes avancées. Installez Shizuku depuis le Play Store. Installer Shizuku - Permission Shizuku + Shizuku permission Required to run power-saving commands while maps is navigating. Accès racine (root) Permissions required for system actions using Root privileges. From 4ba0d4c2fafa9448ac500069c73197a94b7cc967 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 16:02:55 +0530 Subject: [PATCH 007/254] New translations strings.xml (German) --- app/src/main/res/values-de/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 50ec600e..9dc8db88 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -27,7 +27,7 @@ Noch ein Hinweis: Die biometrische Authentifizierungsaufforderung unterstützt ausschließlich sichere Methoden der Klasse STARK. Gesichtserkennungsmethoden der Klasse SCHWACH auf Geräten wie dem Pixel 7 können nur die sonstig verfügbaren STARK-Authentifizierungsmethoden wie Fingerabdruck oder PIN nutzen. Aktiviere Tastenneubelegung - Nutze Shizuku + Use Shizuku Funktioniert mit ausgeschaltetem Display (Empfohlen) Shizuku läuft nicht Erkannte %1$s @@ -379,7 +379,7 @@ Shizuku Benötigt für erweiterte Befehle. Installiere Shizuku aus dem Play Store. Installiere Shizuku - Shizuku-Berechtigung + Shizuku permission Benötigt, um Energiesparbefehle auszuführen, während Maps navigiert. Root-Zugang Benötigt Berechtigungen für Systemaktionen mittels Root-Rechte. @@ -750,7 +750,7 @@ Das Benachrichtigungslicht funktioniert nicht Das hängt vom OEM ab. Einige wie OneUI scheinen keine Überlagerungen über dem Always-On-Display zuzulassen, wodurch die Lichteffekte nicht angezeigt werden können. Versuche in diesem Fall, das Ambient-Display als Workaround zu verwenden. Die Neuzuweisung der Tasten funktioniert nicht, wenn das Display ausgeschaltet ist - Einige OEMs schränken die Barrierefreiheitsdienste ein, sobald das Display ausgeschaltet ist, aber sie funktionieren möglicherweise weiterhin, wenn das Always-on-Display eingeschaltet ist. \nIn diesem Fall kannst du möglicherweise die Tastenbelegung mit eingeschaltetem Always-on-Display verwenden, jedoch nicht mit ausgeschaltetem Always-on-Display. \n\nAls Workaround musst du die Shizuku-Berechtigungen verwenden und in den Einstellungen für die Tastenbelegung die Option „Shizuku verwenden” aktivieren, die Hardware-Eingabeereignisse identifiziert und abhört. \nEs kann nicht garantiert werden, dass dies auf allen Geräten funktioniert, daher muss es getestet werden. \n\nSelbst wenn diese Option aktiviert ist, wird die Shizuku-Methode nur bei Bedarf verwendet. Andernfalls wird immer auf den Barrierefreiheitsdienst zurückgegriffen, welcher auch das Blockieren der tatsächlichen Eingabe bei langem Drücken übernimmt. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Die Helligkeit der Taschenlampe funktioniert nicht Nur eine begrenzte Anzahl von Geräten verfügt über Hardware- und Software-Unterstützung zur Einstellung der Taschenlampenintensität. \n\nDie Mindestversion von Android ist 13 (SDK33).\nDie Helligkeitseinstellung der Taschenlampe unterstützt nur HAL Version 3.8 und höher, daher gehören zu den unterstützten Geräten die neuesten Modelle (z. B. Pixel 6/7, Samsung S23 usw.).\'\npolodarb/Flashlight-Tiramisu Was zum Teufel ist das für eine App? From e4311f9cf60e6327819f2edb037036d93fd8293c Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 16:02:56 +0530 Subject: [PATCH 008/254] New translations strings.xml (Italian) --- app/src/main/res/values-it/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 7b5279c5..11496709 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -28,7 +28,7 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Abilita la rimappatura dei tasti - Usa Shizuku + Use Shizuku Works with screen off (Recommended) Shizuku non è in esecuzione Rilevato %1$s From e6dbb498aa7a8b13934417d802f8914d9737c199 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 16:02:57 +0530 Subject: [PATCH 009/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 7a9ccaa5..f3c0e386 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -754,35 +754,35 @@ フラッシュライトの明るさ調整が機能しません フラッシュライトの明るさ調整をハードウェアとソフトウェアでサポートしているデバイスは限られています。\n\n「Androidの最小バージョンは13 (SDK33) です。\nフラッシュライトの明るさ調整はHALバージョン3.8以降のみをサポートしているため、サポートされている中で最新のもの(例:Pixel 6/7、Samsung S23など)のみが対象です。」\npolodarb/Flashlight-Tiramisu このアプリは一体何ですか? - Good question,\n\nI always wanted to extract the most out of my devices as I\'ve been a rooted user for ever since I got my first Project Treble device. And I\'ve been loving the Tasker app which is like the god when comes automation and utilizing every possible API and internal features of Android.\n\nSo I am not unrooted and back on stock Android beta experience and wanted to get the most out from what is possible with given privileges. Might as well share them. So with my beginner knowledge in Kotlin Jetpack and with the support of many research and assist tools and also the great community, I built an all-in-one app containing everything I wanted to be in my Android with given permissions. And here it is.\n\nFeature requests are welcome, I will consider and see if they are achievable with available permissions and my skills. Nowadays what is not possible. :)\n\nWhy not on Play Store?\nI don\'t wanna risk getting my Developer account banned due to the highly sensitive and internal permissions and APIs being used in the app. But with the way Android sideloading is headed, let\'s see what we have to do. I do understand the concerns of sideloaded apps being malicious.\nWhile we are at the topic, Checkout my other app AirSync if you are a mac + Android user. *shameless plug*\n\nEnjoy, Keep building! (っ◕‿◕)っ + いい質問ですね、\n\n最初のProject Trebleデバイスを入手して以来、ずっとRoot化ユーザーだったので、デバイスを最大限に活用したいと思っていました。そして、自動化やAndroidのあらゆるAPIと内部機能の活用に関しては神のようなTaskerアプリが大好きです。\n\nそのため、Root化を解除し、ストックAndroidベータ版に戻り、与えられた権限でできることを最大限に活用したいと考え、せっかくなので共有したいと思いました。そこで、Kotlin Jetpackの初心者レベルの知識と、多くの調査・支援ツール、そして素晴らしいコミュニティのサポートを受けて、Androidに欲しいものをすべて含んだ、与えられた権限でオールインワンのアプリを構築しました。そしてこれが完成形です。\n\n機能リクエストは歓迎します。利用可能な権限と私のスキルで実現可能かどうか検討します。最近は不可能なことばかりです。:)\n\nなぜPlayストアではだめなのですか?\nアプリで使用されている非常に機密性の高い内部権限とAPIが原因で、開発者アカウントが停止されるリスクがあるからです。しかし、Androidのサイドローディングの方向性を考えると、どうすればよいか見てみましょう。サイドローディングされたアプリが悪意のあるものであるという懸念は理解しています。\nついでに、MacとAndroidの両方をお使いの方は、私の別のアプリ「AirSync」もチェックしてみてください。(宣伝です)\n\nEnjoy, Keep building! (っ◕‿◕)っ バグを報告 - Bug report copied to clipboard - Bug report - Share logs - Include logs and details - Device Info - Raw Report - Open GitHub Issue - Email Report - Copy to Clipboard - Essentials Bug Report - Send via + バグレポートをクリップボードにコピーしました + バグレポート + ログを共有 + ログと詳細を含める + デバイス情報 + Rawレポート + GitHub Issueを開く + メールで報告 + クリップボードにコピー + Essentials Bug Report (バグレポート) + 送信方法 Location reached Get notified when you arrive at a specific destination. - Destination - Set Destination - Tracking: %1$.4f, %2$.4f - No destination set + 目的地 + 目的地をセット + トラッキング中:%1$.4f, %2$.4f + 目的地がセットされていません Open your map app, pick a location, and share it to Essentials. Radius: %d m - Distance to trigger the alarm - Enable notification - Location - Used to detect arrival at your destination. - Background Location - Required to monitor your arrival while the app is closed or the screen is off. + 知らせる距離 + 通知オン + 位置情報 + 目的地への到着を検出するために使用します。 + バックグラウンド位置情報 + アプリが閉じているときや画面がオフのときに到着を監視するために必要です。 Destination Reached! You have arrived at your destination. Currently %1$.1f km away From 825272067dfcd5fded0360b346c17b57b498714e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 16:02:58 +0530 Subject: [PATCH 010/254] New translations strings.xml (Russian) --- app/src/main/res/values-ru/strings.xml | 142 ++++++++++++------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 2828ec5d..d99928dc 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,58 +1,58 @@ Essentials - BETA - Essentials Accessibility Service\n\nThis service is required for the following advanced features:\n\n• Physical Button Remapping:\nDetects volume button presses even when the screen is off to trigger actions like the Flashlight.\n\n• Per-App Settings:\nMonitors the currently active app to apply specific profiles for Dynamic Night Light, Notification Lighting Colors, and App Lock.\n\n• Screen Control:\nAllows the app to lock the screen (e.g. via Double Tap or Widgets) and detect screen state changes.\n\n• Security:\nPrevents unauthorized changes by detecting window content when the device is locked.\n\nNo input text or sensitive user data is collected or transmitted. + БЕТА + Служба специальных возможностей Essentials\n\nЭта служба необходима для следующих функций:\n\n• Переназначение физических кнопок:\nОбнаруживает нажатие кнопок громкости, даже если экран выключен, чтобы вызывать такие действия, как фонарик.\n\n• Индивидуальные настройки приложений:\nОтслеживает текущее активное приложение для применения конкретных профилей для Динамической ночной подсветки, Цвета подсветки уведомлений и блокировку приложений.\n\n• Контроль экрана:\nПозволяет приложению блокировать экран (например, через двойное нажатие или виджет) и обнаруживать изменения состояния экрана.\n\n• Безопасность:\nПредотвращает несанкционированные изменения, обнаруживая содержимое окна, когда устройство заблокировано.\n\nНикакой вводимый текст или конфиденциальные данные не собираются и не передаются. App icon App Freezing - Disable apps that are rarely used - App Freezing - Open App Freezing + Отключить редко используемые приложения + Заморозка приложений + Открыть заморозку приложений Frozen App - Empty screen off widget - App Freezing + Пустой виджет для отключения экрана + Заморозка приложений Flashlight Pulse - Check for pre-releases - Might be unstable + Проверять наличие предварительных релизов + Могут быть нестабильны - Security - Enable app lock + Безопасность + Включить блокировку приложений App Lock Security - Authenticate to enable app lock - Authenticate to disable app lock - Select locked apps - Choose which apps require authentication - Secure your apps with biometric authentication. Locked apps will require authentication when launching, Stays unlocked until the screen turns off. - Beware that this is not a robust solution as this is only a 3rd party application. If you need strong security, consider using Private Space or other such features. - Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. + Подтвердите свою личность, чтобы включить блокировку приложений + Подтвердите свою личность, чтобы отключить блокировку приложения + Выбрать заблокированные приложения + Выберите, какие приложения будут требовать проверку доступа + Защитите свои приложения с помощью биометрической аутентификации. Заблокированные приложения требуют аутентификации при запуске и остаются разблокированными до тех пор, пока экран не погаснет. + Имейте в виду, что это не надежное решение, поскольку это всего лишь стороннее приложение. Если вам нужна надежная защита, рассмотрите возможность использования личного пространства или других подобных функций. + Обратите внимание, что запрос на биометрическую аутентификацию позволяет использовать только методы класса STRONG. Методы разблокировки по лицу относятся к классу WEAK. Поэтому устройства, например Pixel 7, смогут использовать только другие доступные методы надежной аутентификации, такие как отпечаток пальца или pin-код. - Enable Button Remap + Включить переназначение кнопок Use Shizuku - Works with screen off (Recommended) - Shizuku is not running - Detected %1$s - Status: %1$s - Open Shizuku - Flashlight - Flashlight options - Adjust fading and other settings - Pitch black theme - Use pure black background in dark mode - Haptic Feedback - Remap Long Press - Screen Off - Screen On - Volume Up - Volume Down - Toggle flashlight - Media play/pause - Media next - Media previous - Toggle vibrate - Toggle mute - AI assistant - Take screenshot - When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. + Работает если экран выключен (рекомендуется) + Shizuki не запущен + Обнаружен %1$s + Статус: %1$s + Открыть Shizuku + Фонарик + Настройки фонарика + Отрегулируйте затухание и другие настройки + Чёрная тема + Использовать чистый чёрный фон в тёмной теме + Тактильная обратная связь + Переназначить долгое нажатие + Отключить экран + Включить экран + Увеличение громкости + Уменьшение громкости + Фонарик + Воспроизведение/пауза + Следующее медиа + Предыдущее медиа + Вибрация + Без звука + AI ассистент + Сделать снимок экрана + Когда экран выключен, нажмите и удерживайте выбранную кнопку, чтобы активировать назначенное действие. На устройствах Pixel это действие запускается только при включенном AOD из-за системных ограничений. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity Fade in and out @@ -114,43 +114,43 @@ Freeze selected apps when the device locks. Choose a delay to avoid freezing apps if you unlock the screen shortly after turning it off. Freezing system apps might be dangerous and may cause unexpected behavior. Freeze when locked - Accessibility Service + Служба специальных возможностей Required to detect screen state for automatic freezing. - Enable in Settings + Включить в настройках - Only show when screen off - Skip silent notifications - Skip persistent notifications + Показывать только если экран выключен + Пропускать уведомления без звука + Пропускать постоянные уведомления Flashlight Pulse Flashlight pulse - Only while facing down + Только экраном вниз Style Stroke adjustment - Corner radius + Радиус угла Stroke thickness - Glow adjustment + Регулировка свечения Glow spread - Placement - Horizontal position - Vertical position - Indicator adjustment - Scale - Duration + Размещение + Горизонтальная позиция + Вертикальная позиция + Регулировка индикатора + Размер + Продолжительность Animation Pulse count Pulse duration - Color Mode + Цвет Ambient display Ambient display - Suitable if you are not using AOD. - Wake screen and show lighting - Show lock screen - No black overlay + Подходит, если вы не используете AOD. + Будит экран и включает подсветку + Показать экран блокировки + Без чёрного фона - Add - Already added - Requires Android 13+ - UI Blur + Добавить + Уже добавлено + Требуется Android 13+ + Размытие интерфейса Bubbles Sensitive Content Tap to Wake @@ -178,10 +178,10 @@ Screen Locked Security Authenticate to enable screen locked security Authenticate to disable screen locked security - ⚠️ WARNING - This feature is not foolproof. There may be edge cases where someone still being able to interact with the tile. \nAlso keep in mind that Android will always allow to do a forced reboot and Pixels will always allow the device to be turned off from the lock screen as well. - Make sure to remove the airplane mode tile from quick settings as that is not preventable because it does not open a dialog window. - When enabled, the Quick Settings panel will be immediately closed and the device will be locked down if someone attempt to interact with Internet tiles while the device is locked. \n\nThis will also disable biometric unlock to prevent further unauthorized access. Animation scale will be reduced to 0.1x while locked to make it even harder to interact with. + ⚠️ ВНИМАНИЕ + Эта функция не является надежной. В некоторых случаях пользователь все еще может взаимодействовать с плиткой. \nТакже имейте в виду, что Android всегда разрешает принудительную перезагрузку, а Пиксели всегда позволяют отключить устройство с экрана блокировки. + Обязательно удалите плитку \"Режим полета\" из \"быстрых настроек\", так как это невозможно предотвратить, поскольку она не открывает диалоговое окно. + Если эта функция включена, панель быстрых настроек будет немедленно закрыта, а устройство заблокировано, если кто-либо попытается взаимодействовать с интернет-плитками, пока устройство заблокировано. \n\nЭто также отключит биометрическую разблокировку, чтобы предотвратить дальнейший несанкционированный доступ. Во время блокировки масштаб анимации будет уменьшен до 0.1x, что еще больше усложнит взаимодействие с устройством. Re-order modes Long press to toggle From 8054ccea39e22b57e2919302a1e04bc92846272a Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 16:59:53 +0530 Subject: [PATCH 011/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index f3c0e386..18dd74cb 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -783,8 +783,8 @@ 目的地への到着を検出するために使用します。 バックグラウンド位置情報 アプリが閉じているときや画面がオフのときに到着を監視するために必要です。 - Destination Reached! - You have arrived at your destination. + 目的地に到着しました! + あなたは目的地に到着しました。 Currently %1$.1f km away Clear Destination Processing location… From bc6c4a314e857ebaf5ec20768b1a095e538ef56d Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 16:59:54 +0530 Subject: [PATCH 012/254] New translations strings.xml (Russian) --- app/src/main/res/values-ru/strings.xml | 68 +++++++++++++------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index d99928dc..6cf1e5a8 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -17,11 +17,11 @@ Безопасность Включить блокировку приложений - App Lock Security + Защита блокировки экрана Подтвердите свою личность, чтобы включить блокировку приложений Подтвердите свою личность, чтобы отключить блокировку приложения Выбрать заблокированные приложения - Выберите, какие приложения будут требовать проверку доступа + Выберите, какие приложения будут требовать аутентификации Защитите свои приложения с помощью биометрической аутентификации. Заблокированные приложения требуют аутентификации при запуске и остаются разблокированными до тех пор, пока экран не погаснет. Имейте в виду, что это не надежное решение, поскольку это всего лишь стороннее приложение. Если вам нужна надежная защита, рассмотрите возможность использования личного пространства или других подобных функций. Обратите внимание, что запрос на биометрическую аутентификацию позволяет использовать только методы класса STRONG. Методы разблокировки по лицу относятся к классу WEAK. Поэтому устройства, например Pixel 7, смогут использовать только другие доступные методы надежной аутентификации, такие как отпечаток пальца или pin-код. @@ -53,33 +53,33 @@ AI ассистент Сделать снимок экрана Когда экран выключен, нажмите и удерживайте выбранную кнопку, чтобы активировать назначенное действие. На устройствах Pixel это действие запускается только при включенном AOD из-за системных ограничений. - When the screen is on, long-press the selected button to trigger its assigned action. - Flashlight Intensity - Fade in and out - Smoothly toggle flashlight - Global controls + Когда экран включен, нажмите и удерживайте выбранную кнопку, чтобы активировать назначенное ей действие. + Яркость фонарика + Плавное включение и выключение + Плавное переключение фонарика + Глобальное управление Fade-in flashlight globally - Adjust intensity - Volume + - adjusts flashlight intensity - Live update - Show brightness in status bar - Other - Always turn off flashlight - Even while display is on - Settings + Регулировка яркости + Громкость + - регулирует яркость фонарика + Индикатор яркости + Показывает яркость в строке состояния + Другое + Всегда выключать фонарик + Даже при включенном экране + Настройки - Notification - Show Notification - Post Notifications - Allows the app to show notifications - Grant Permission - Caffeinate Active - Active - Screen is being kept awake - Ignore battery optimization - Recommended for reliable service on some devices - Abort with screen off - Timeout Presets + Уведомления + Показывать уведомления + Отправка уведомлений + Разрешить приложению отправку уведомлений + Выдать разрешение + Caffeinate активен + Активен + Экран остаётся включенным + Игнорировать оптимизацию батареи + Рекомендуется для надежной работы на некоторых устройствах + Прервать если экран выключен + Предустановки времени Select available durations for QS tile 5m 10m @@ -181,14 +181,14 @@ ⚠️ ВНИМАНИЕ Эта функция не является надежной. В некоторых случаях пользователь все еще может взаимодействовать с плиткой. \nТакже имейте в виду, что Android всегда разрешает принудительную перезагрузку, а Пиксели всегда позволяют отключить устройство с экрана блокировки. Обязательно удалите плитку \"Режим полета\" из \"быстрых настроек\", так как это невозможно предотвратить, поскольку она не открывает диалоговое окно. - Если эта функция включена, панель быстрых настроек будет немедленно закрыта, а устройство заблокировано, если кто-либо попытается взаимодействовать с интернет-плитками, пока устройство заблокировано. \n\nЭто также отключит биометрическую разблокировку, чтобы предотвратить дальнейший несанкционированный доступ. Во время блокировки масштаб анимации будет уменьшен до 0.1x, что еще больше усложнит взаимодействие с устройством. + Если эта функция включена, панель быстрых настроек будет немедленно закрыта, а устройство заблокировано, если кто-либо попытается взаимодействовать с интернет-плитками, пока устройство заблокировано. \n\nЭто также отключит биометрическую разблокировку, чтобы предотвратить дальнейший несанкционированный доступ. Во время блокировки анимации будут замедлены до 0.1x, что еще больше усложнит взаимодействие с устройством. - Re-order modes - Long press to toggle - Drag to reorder - Sound - Vibrate - Silent + Изменить порядок режимов + Долгое нажатие для переключения + Перетащите, чтобы изменить порядок + Звук + Вибрация + Без звука Connectivity Phone & Network From 5ca3af3743e56cd9b47748886f09bcf3537aa188 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Sat, 17 Jan 2026 17:34:54 +0530 Subject: [PATCH 013/254] App automations --- .../essentials/domain/diy/Automation.kt | 6 +- .../services/automation/AutomationManager.kt | 3 + .../services/handlers/AppFlowHandler.kt | 47 +++ .../ui/activities/AutomationEditorActivity.kt | 269 ++++++++++++++---- .../ui/components/diy/AutomationItem.kt | 19 +- .../components/sheets/NewAutomationSheet.kt | 8 + app/src/main/res/values/strings.xml | 2 + 7 files changed, 290 insertions(+), 64 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/domain/diy/Automation.kt b/app/src/main/java/com/sameerasw/essentials/domain/diy/Automation.kt index 94e3c71f..b2ef49ee 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/diy/Automation.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/diy/Automation.kt @@ -8,10 +8,12 @@ data class Automation( val actions: List = emptyList(), val entryAction: Action? = null, val exitAction: Action? = null, - val isEnabled: Boolean = true + val isEnabled: Boolean = true, + val selectedApps: List = emptyList() ) { enum class Type { TRIGGER, - STATE + STATE, + APP } } diff --git a/app/src/main/java/com/sameerasw/essentials/services/automation/AutomationManager.kt b/app/src/main/java/com/sameerasw/essentials/services/automation/AutomationManager.kt index 6ad80e23..71ed32e1 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/automation/AutomationManager.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/automation/AutomationManager.kt @@ -90,6 +90,9 @@ object AutomationManager { else -> {} } } + Automation.Type.APP -> { + // Handled by AppFlowHandler + } } } diff --git a/app/src/main/java/com/sameerasw/essentials/services/handlers/AppFlowHandler.kt b/app/src/main/java/com/sameerasw/essentials/services/handlers/AppFlowHandler.kt index 457fa726..94f903aa 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/handlers/AppFlowHandler.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/handlers/AppFlowHandler.kt @@ -12,10 +12,18 @@ import com.google.gson.Gson import com.sameerasw.essentials.domain.model.AppSelection import com.google.gson.reflect.TypeToken +import com.sameerasw.essentials.domain.diy.Automation +import com.sameerasw.essentials.domain.diy.DIYRepository +import com.sameerasw.essentials.services.automation.executors.CombinedActionExecutor +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + class AppFlowHandler( private val service: AccessibilityService ) { private val handler = Handler(Looper.getMainLooper()) + private val scope = CoroutineScope(Dispatchers.Main) private val authenticatedPackages = mutableSetOf() @@ -23,6 +31,9 @@ class AppFlowHandler( private var lockingPackage: String? = null private var lastLockRequestTime: Long = 0 + // App Automation State + private val activeAppAutomationIds = mutableSetOf() + // Night Light State private var wasNightLightOnBeforeAutoToggle = false private var isNightLightAutoToggledOff = false @@ -31,6 +42,8 @@ class AppFlowHandler( private val ignoredSystemPackages = listOf( "android", + "com.android.systemui", + "com.google.android.inputmethod.latin" ) fun onPackageChanged(packageName: String) { @@ -40,6 +53,7 @@ class AppFlowHandler( checkAppLock(packageName) checkHighlightNightLight(packageName) + checkAppAutomations(packageName) } fun onAuthenticated(packageName: String) { @@ -163,4 +177,37 @@ class AppFlowHandler( Log.w("NightLight", "Failed to set night light: ${e.message}. Ensure WRITE_SECURE_SETTINGS is granted.") } } + + private fun checkAppAutomations(packageName: String) { + scope.launch { + val automations = DIYRepository.automations.value + val appAutomations = automations.filter { it.isEnabled && it.type == Automation.Type.APP } + + // Exiting Automations + // An automation is exiting if it was active, but the new package is NOT in its selected apps list + val exiting = appAutomations.filter { + activeAppAutomationIds.contains(it.id) && !it.selectedApps.contains(packageName) + } + + exiting.forEach { automation -> + activeAppAutomationIds.remove(automation.id) + automation.exitAction?.let { action -> + CombinedActionExecutor.execute(service, action) + } + } + + // Entering Automations + // An automation is entering if it was NOT active, and the new package IS in its selected apps list + val entering = appAutomations.filter { + !activeAppAutomationIds.contains(it.id) && it.selectedApps.contains(packageName) + } + + entering.forEach { automation -> + activeAppAutomationIds.add(automation.id) + automation.entryAction?.let { action -> + CombinedActionExecutor.execute(service, action) + } + } + } + } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/activities/AutomationEditorActivity.kt b/app/src/main/java/com/sameerasw/essentials/ui/activities/AutomationEditorActivity.kt index bb0b67cb..926f8b59 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/activities/AutomationEditorActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/activities/AutomationEditorActivity.kt @@ -69,6 +69,18 @@ import com.sameerasw.essentials.ui.components.sheets.DimWallpaperSettingsSheet import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow import com.sameerasw.essentials.utils.HapticUtil +import com.sameerasw.essentials.domain.model.AppSelection +import com.sameerasw.essentials.domain.model.NotificationApp +import com.sameerasw.essentials.utils.AppUtil +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.LoadingIndicator +import androidx.compose.material3.Switch +import androidx.compose.ui.draw.clip +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import com.sameerasw.essentials.ui.components.cards.AppToggleItem class AutomationEditorActivity : ComponentActivity() { @@ -104,7 +116,7 @@ class AutomationEditorActivity : ComponentActivity() { val isEditMode = existingAutomation != null val automationType = if (isEditMode) { - existingAutomation?.type ?: Automation.Type.TRIGGER + existingAutomation.type } else { try { Automation.Type.valueOf(automationTypeStr ?: Automation.Type.TRIGGER.name) @@ -113,7 +125,11 @@ class AutomationEditorActivity : ComponentActivity() { } } - val titleRes = if (isEditMode) R.string.diy_editor_edit_title else R.string.diy_editor_new_title + val titleRes = when(automationType) { + Automation.Type.TRIGGER -> if (isEditMode) R.string.diy_editor_edit_title else R.string.diy_editor_new_title + Automation.Type.STATE -> if (isEditMode) R.string.diy_editor_edit_title else R.string.diy_editor_new_title + Automation.Type.APP -> if (isEditMode) R.string.diy_editor_edit_title else R.string.diy_create_app_title + } setContent { val viewModel: com.sameerasw.essentials.viewmodels.MainViewModel = androidx.lifecycle.viewmodel.compose.viewModel() @@ -143,6 +159,42 @@ class AutomationEditorActivity : ComponentActivity() { // Initialize with existing data or defaults var selectedTrigger by remember { mutableStateOf(existingAutomation?.trigger) } var selectedState by remember { mutableStateOf(existingAutomation?.state) } + var selectedApps by remember { mutableStateOf>(existingAutomation?.selectedApps ?: emptyList()) } + + // App Picker State + var searchQuery by remember { mutableStateOf("") } + var allApps by remember { mutableStateOf>(emptyList()) } + var isLoadingApps by remember { mutableStateOf(false) } + var showSystemApps by remember { mutableStateOf(false) } + + // Load apps if needed + LaunchedEffect(automationType) { + if (automationType == Automation.Type.APP) { + isLoadingApps = true + withContext(Dispatchers.IO) { + try { + val installed = AppUtil.getInstalledApps(context) + // Merge with selection if existing + val merged = AppUtil.mergeWithSavedApps(installed, selectedApps.map { AppSelection(it, true) }) + withContext(Dispatchers.Main) { + allApps = merged + isLoadingApps = false + } + } catch (e: Exception) { + e.printStackTrace() + withContext(Dispatchers.Main) { isLoadingApps = false } + } + } + } + } + + val filteredApps = remember(allApps, searchQuery, showSystemApps, selectedApps) { + allApps.filter { + val matchesSearch = searchQuery.isEmpty() || it.appName.contains(searchQuery, ignoreCase = true) + val isVisible = !it.isSystemApp || showSystemApps || selectedApps.contains(it.packageName) + matchesSearch && isVisible + }.sortedWith(compareByDescending { selectedApps.contains(it.packageName) }.thenBy { it.appName.lowercase() }) + } // Actions // For Trigger type @@ -166,6 +218,7 @@ class AutomationEditorActivity : ComponentActivity() { val isValid = when (automationType) { Automation.Type.TRIGGER -> selectedTrigger != null && selectedAction != null Automation.Type.STATE -> selectedState != null && (selectedInAction != null || selectedOutAction != null) + Automation.Type.APP -> selectedApps.isNotEmpty() && (selectedInAction != null || selectedOutAction != null) } Scaffold( @@ -194,7 +247,7 @@ class AutomationEditorActivity : ComponentActivity() { text = { Text(stringResource(R.string.action_delete)) }, onClick = { showMenu = false - DIYRepository.removeAutomation(existingAutomation!!.id) + DIYRepository.removeAutomation(existingAutomation.id) finish() }, leadingIcon = { @@ -222,7 +275,7 @@ class AutomationEditorActivity : ComponentActivity() { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { // Only handle drag (user interaction) - if (source == NestedScrollSource.Drag) { + if (source == NestedScrollSource.UserInput) { accumulatedScroll += available.x if (kotlin.math.abs(accumulatedScroll) >= threshold) { @@ -257,54 +310,148 @@ class AutomationEditorActivity : ComponentActivity() { ) { if (index == 0) { // PAGE 0: Trigger or State Picker - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - Text( - text = stringResource(if (automationType == Automation.Type.TRIGGER) R.string.diy_select_trigger else R.string.diy_select_state), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.padding(horizontal = 12.dp) - ) - - RoundedCardContainer(spacing = 2.dp) { - if (automationType == Automation.Type.TRIGGER) { - val triggers = listOf( - Trigger.ScreenOff, - Trigger.ScreenOn, - Trigger.DeviceUnlock, - Trigger.ChargerConnected, - Trigger.ChargerDisconnected - ) - triggers.forEach { trigger -> - EditorActionItem( - title = stringResource(trigger.title), - iconRes = trigger.icon, - isSelected = selectedTrigger == trigger, - isConfigurable = trigger.isConfigurable, - onClick = { selectedTrigger = trigger }, - onSettingsClick = { - // Handle trigger settings if needed later - } + if (automationType == Automation.Type.APP) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = stringResource(R.string.diy_create_app_title), + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.padding(horizontal = 12.dp) + ) + + // Search Bar + OutlinedTextField( + value = searchQuery, + onValueChange = { searchQuery = it }, + modifier = Modifier.fillMaxWidth(), + placeholder = { Text(stringResource(R.string.label_search)) }, + leadingIcon = { + Icon( + painter = painterResource(id = R.drawable.rounded_search_24), + contentDescription = stringResource(R.string.action_search) ) + }, + singleLine = true, + shape = RoundedCornerShape(12.dp) + ) + + // System Apps Toggle + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .clickable { + HapticUtil.performVirtualKeyHaptic(view) + showSystemApps = !showSystemApps + } + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Icon( + painter = painterResource(id = R.drawable.rounded_settings_24), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + Text( + text = stringResource(R.string.toggle_show_system_apps), + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.weight(1f), + color = MaterialTheme.colorScheme.onSurface + ) + Switch( + checked = showSystemApps, + onCheckedChange = { + HapticUtil.performVirtualKeyHaptic(view) + showSystemApps = it + } + ) + } + + if (isLoadingApps) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + LoadingIndicator() } } else { - val states = listOf( - DIYState.Charging, - DIYState.ScreenOn - ) - states.forEach { state -> - EditorActionItem( - title = stringResource(state.title), - iconRes = state.icon, - isSelected = selectedState == state, - onClick = { selectedState = state } - ) + LazyColumn( + modifier = Modifier + .weight(1f) + .clip(RoundedCornerShape(24.dp)), + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + items(filteredApps, key = { it.packageName }) { app -> + val isSelected = selectedApps.contains(app.packageName) + AppToggleItem( + icon = app.icon, + title = app.appName, + isChecked = isSelected, + onCheckedChange = { isChecked -> + val current = selectedApps.toMutableList() + if (isChecked) current.add(app.packageName) else current.remove(app.packageName) + selectedApps = current + } + ) + } + } + } + } + } else { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = stringResource(if (automationType == Automation.Type.TRIGGER) R.string.diy_select_trigger else R.string.diy_select_state), + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.padding(horizontal = 12.dp) + ) + + RoundedCardContainer(spacing = 2.dp) { + if (automationType == Automation.Type.TRIGGER) { + val triggers = listOf( + Trigger.ScreenOff, + Trigger.ScreenOn, + Trigger.DeviceUnlock, + Trigger.ChargerConnected, + Trigger.ChargerDisconnected + ) + triggers.forEach { trigger -> + EditorActionItem( + title = stringResource(trigger.title), + iconRes = trigger.icon, + isSelected = selectedTrigger == trigger, + isConfigurable = trigger.isConfigurable, + onClick = { selectedTrigger = trigger }, + onSettingsClick = { + // Handle trigger settings if needed later + } + ) + } + } else { + val states = listOf( + DIYState.Charging, + DIYState.ScreenOn + ) + states.forEach { state -> + EditorActionItem( + title = stringResource(state.title), + iconRes = state.icon, + isSelected = selectedState == state, + onClick = { selectedState = state } + ) + } } } } @@ -326,7 +473,7 @@ class AutomationEditorActivity : ComponentActivity() { modifier = Modifier.padding(horizontal = 12.dp) ) - if (automationType == Automation.Type.STATE) { + if (automationType == Automation.Type.STATE || automationType == Automation.Type.APP) { // Tabs for In/Out val options = listOf( stringResource(R.string.diy_in_action_label), @@ -357,6 +504,7 @@ class AutomationEditorActivity : ComponentActivity() { val currentSelection = when(automationType) { Automation.Type.TRIGGER -> selectedAction Automation.Type.STATE -> if (selectedActionTab == 0) selectedInAction else selectedOutAction + Automation.Type.APP -> if (selectedActionTab == 0) selectedInAction else selectedOutAction } // None option @@ -367,7 +515,7 @@ class AutomationEditorActivity : ComponentActivity() { onClick = { when(automationType) { Automation.Type.TRIGGER -> selectedAction = null - Automation.Type.STATE -> { + Automation.Type.STATE, Automation.Type.APP -> { if (selectedActionTab == 0) selectedInAction = null else selectedOutAction = null } @@ -388,7 +536,7 @@ class AutomationEditorActivity : ComponentActivity() { onClick = { when(automationType) { Automation.Type.TRIGGER -> selectedAction = resolvedAction - Automation.Type.STATE -> { + Automation.Type.STATE, Automation.Type.APP -> { if (selectedActionTab == 0) selectedInAction = resolvedAction else selectedOutAction = resolvedAction } @@ -417,7 +565,7 @@ class AutomationEditorActivity : ComponentActivity() { // Update the selection with configured action when(automationType) { Automation.Type.TRIGGER -> selectedAction = newAction - Automation.Type.STATE -> { + Automation.Type.STATE, Automation.Type.APP -> { if (selectedActionTab == 0) selectedInAction = newAction else selectedOutAction = newAction } @@ -460,21 +608,30 @@ class AutomationEditorActivity : ComponentActivity() { // Save logic if (automationType == Automation.Type.TRIGGER) { val newAutomation = Automation( - id = if (isEditMode) existingAutomation!!.id else java.util.UUID.randomUUID().toString(), + id = if (isEditMode) existingAutomation.id else java.util.UUID.randomUUID().toString(), type = Automation.Type.TRIGGER, trigger = selectedTrigger, actions = listOfNotNull(selectedAction) ) if (isEditMode) DIYRepository.updateAutomation(newAutomation) else DIYRepository.addAutomation(newAutomation) - } else { + } else if (automationType == Automation.Type.STATE) { val newAutomation = Automation( - id = if (isEditMode) existingAutomation!!.id else java.util.UUID.randomUUID().toString(), + id = if (isEditMode) existingAutomation.id else java.util.UUID.randomUUID().toString(), type = Automation.Type.STATE, state = selectedState, entryAction = selectedInAction, exitAction = selectedOutAction ) if (isEditMode) DIYRepository.updateAutomation(newAutomation) else DIYRepository.addAutomation(newAutomation) + } else { + val newAutomation = Automation( + id = if (isEditMode) existingAutomation.id else java.util.UUID.randomUUID().toString(), + type = Automation.Type.APP, + selectedApps = selectedApps, + entryAction = selectedInAction, + exitAction = selectedOutAction + ) + if (isEditMode) DIYRepository.updateAutomation(newAutomation) else DIYRepository.addAutomation(newAutomation) } finish() }, diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/diy/AutomationItem.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/diy/AutomationItem.kt index c786f7d5..b0c3f8da 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/diy/AutomationItem.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/diy/AutomationItem.kt @@ -151,12 +151,19 @@ fun AutomationItem( .weight(1f) .fillMaxHeight(), ) { - val icon = - if (automation.type == Automation.Type.TRIGGER) automation.trigger?.icon else automation.state?.icon - val title = - if (automation.type == Automation.Type.TRIGGER) automation.trigger?.title else automation.state?.title + val icon = when (automation.type) { + Automation.Type.TRIGGER -> automation.trigger?.icon + Automation.Type.STATE -> automation.state?.icon + Automation.Type.APP -> R.drawable.rounded_apps_24 + } + + val titleString = when (automation.type) { + Automation.Type.TRIGGER -> automation.trigger?.title?.let { stringResource(it) } + Automation.Type.STATE -> automation.state?.title?.let { stringResource(it) } + Automation.Type.APP -> stringResource(R.string.diy_create_app_title) + " (${automation.selectedApps.size})" + } - if (icon != null && title != null) { + if (icon != null && titleString != null) { Surface( color = MaterialTheme.colorScheme.secondaryContainer, shape = RoundedCornerShape(16.dp), @@ -185,7 +192,7 @@ fun AutomationItem( ) Spacer(modifier = Modifier.width(12.dp)) Text( - text = stringResource(id = title), + text = titleString, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSecondaryContainer, fontWeight = FontWeight.SemiBold, diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/NewAutomationSheet.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/NewAutomationSheet.kt index 215961b3..4b0674d9 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/NewAutomationSheet.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/NewAutomationSheet.kt @@ -91,6 +91,14 @@ fun NewAutomationSheet( iconRes = R.drawable.rounded_toggle_on_24, onClick = { onOptionSelected(Automation.Type.STATE) } ) + + // App Option + AutomationTypeOption( + title = stringResource(R.string.diy_create_app_title), + description = stringResource(R.string.diy_create_app_desc), + iconRes = R.drawable.rounded_apps_24, + onClick = { onOptionSelected(Automation.Type.APP) } + ) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a23d06cd..bad5805e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -492,6 +492,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action From aaaf813aaac414109ba57047fb8c2a8e60848868 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 18:12:40 +0530 Subject: [PATCH 014/254] New translations strings.xml (Russian) --- app/src/main/res/values-ru/strings.xml | 32 +++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6cf1e5a8..349fe98b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -79,27 +79,27 @@ Игнорировать оптимизацию батареи Рекомендуется для надежной работы на некоторых устройствах Прервать если экран выключен - Предустановки времени - Select available durations for QS tile - 5m - 10m - 30m - 1h + Интервалы работы + Выберите доступные интервалы для плитки быстрого доступа + + 10м + 30м + - Starting in %1$ds… - %1$s remaining - Persistent notification for Caffeinate + Запуск через %1$ds… + %1$s осталось + Постоянное уведомление для Caffeinate - Enable Dynamic Night Light - Apps that toggle off night light - Select apps + Включает динамическую ночную подсветку + Приложения, отключающие ночную подсветку + Выбрать приложения App Control - Freeze - Unfreeze + Заморозить + Разморозить More options - Freeze all apps - Unfreeze all apps + Заморозить все приложения + Разморозить все приложения Pick apps to freeze Choose which apps can be frozen Automation From 7aad5df1b9fe153f81feaf48c76fc1455eeea0de Mon Sep 17 00:00:00 2001 From: sameerasw Date: Sat, 17 Jan 2026 19:11:27 +0530 Subject: [PATCH 015/254] Device effects action and expressive sheets for edit diy --- app/src/main/AndroidManifest.xml | 10 + .../sameerasw/essentials/domain/diy/Action.kt | 15 + .../services/EssentialsConditionProvider.kt | 65 ++++ .../executors/CombinedActionExecutor.kt | 61 ++++ .../ui/activities/AutomationEditorActivity.kt | 50 ++- .../sheets/DeviceEffectsSettingsSheet.kt | 307 ++++++++++++++++++ .../sheets/DimWallpaperSettingsSheet.kt | 161 ++++++--- .../main/res/drawable/round_king_bed_24.xml | 5 + app/src/main/res/drawable/rounded_bed_24.xml | 5 + .../main/res/drawable/rounded_cancel_24.xml | 5 + .../res/drawable/rounded_check_circle_24.xml | 5 + app/src/main/res/values/strings.xml | 11 + 12 files changed, 638 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/com/sameerasw/essentials/services/EssentialsConditionProvider.kt create mode 100644 app/src/main/java/com/sameerasw/essentials/ui/components/sheets/DeviceEffectsSettingsSheet.kt create mode 100644 app/src/main/res/drawable/round_king_bed_24.xml create mode 100644 app/src/main/res/drawable/rounded_bed_24.xml create mode 100644 app/src/main/res/drawable/rounded_cancel_24.xml create mode 100644 app/src/main/res/drawable/rounded_check_circle_24.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eff3fc25..c2359cc1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -434,6 +434,16 @@ android:value="Automation Service" /> + + + + + + = listOf("shizuku", "root") override val isConfigurable: Boolean = true } + + data class DeviceEffects( + val enabled: Boolean = true, + val grayscale: Boolean = false, + val suppressAmbient: Boolean = false, + val dimWallpaper: Boolean = false, + val nightMode: Boolean = false + ) : Action { + override val title: Int get() = R.string.diy_action_device_effects + override val icon: Int get() = R.drawable.rounded_bed_24 + override val permissions: List = listOf("notification_policy") + override val isConfigurable: Boolean = true + } + + } diff --git a/app/src/main/java/com/sameerasw/essentials/services/EssentialsConditionProvider.kt b/app/src/main/java/com/sameerasw/essentials/services/EssentialsConditionProvider.kt new file mode 100644 index 00000000..83cd9a2f --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/services/EssentialsConditionProvider.kt @@ -0,0 +1,65 @@ +package com.sameerasw.essentials.services + +import android.content.ComponentName +import android.content.Context +import android.content.pm.PackageManager +import android.net.Uri +import android.service.notification.Condition +import android.service.notification.ConditionProviderService +import android.util.Log + +class EssentialsConditionProvider : ConditionProviderService() { + + companion object { + private const val TAG = "EssentialsCPS" + val CONDITION_URI: Uri = Uri.parse("condition://com.sameerasw.essentials/focus") + + private var instance: EssentialsConditionProvider? = null + + fun setConditionState(context: Context, isActive: Boolean) { + Log.d(TAG, "Requesting condition state: $isActive") + val provider = instance + if (provider != null) { + provider.notifyChange(isActive) + } else { + try { + requestRebind(ComponentName(context, EssentialsConditionProvider::class.java)) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + } + + override fun onConnected() { + + Log.d(TAG, "onConnected") + instance = this + } + + override fun onDestroy() { + super.onDestroy() + Log.d(TAG, "onDestroy") + instance = null + } + + override fun onSubscribe(conditionId: Uri?) { + Log.d(TAG, "onSubscribe: $conditionId") + notifyChange(false) + } + + override fun onUnsubscribe(conditionId: Uri?) { + Log.d(TAG, "onUnsubscribe: $conditionId") + } + + private fun notifyChange(active: Boolean) { + Log.d(TAG, "notifyChange: active=$active") + val state = if (active) Condition.STATE_TRUE else Condition.STATE_FALSE + val condition = Condition(CONDITION_URI, "Essentials Focus", state) + try { + notifyCondition(condition) + } catch (e: Exception) { + Log.e(TAG, "Failed to notify condition", e) + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/services/automation/executors/CombinedActionExecutor.kt b/app/src/main/java/com/sameerasw/essentials/services/automation/executors/CombinedActionExecutor.kt index 3f3c496b..59a9c3b4 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/automation/executors/CombinedActionExecutor.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/automation/executors/CombinedActionExecutor.kt @@ -60,6 +60,67 @@ object CombinedActionExecutor { is Action.DimWallpaper -> { com.sameerasw.essentials.utils.ShellUtils.runCommand(context, "cmd wallpaper set-dim-amount ${action.dimAmount}") } + is Action.DeviceEffects -> { + if (Build.VERSION.SDK_INT >= 35) { // Android 15+ + val nm = context.getSystemService(android.app.NotificationManager::class.java) + if (nm.isNotificationPolicyAccessGranted) { + try { + if (action.enabled) { + // ENABLE/UPDATE EFFECTS + val effectsBuilder = android.service.notification.ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale(action.grayscale) + .setShouldSuppressAmbientDisplay(action.suppressAmbient) + .setShouldDimWallpaper(action.dimWallpaper) + .setShouldUseNightMode(action.nightMode) + + val effects = effectsBuilder.build() + + val ruleId = "essentials_focus_mode" + val existingRule = nm.automaticZenRules.values.find { it.name == "Essentials Focus" } + val ruleKey = existingRule?.let { nm.automaticZenRules.entries.find { entry -> entry.value == it }?.key } + + val componentName = android.content.ComponentName(context, com.sameerasw.essentials.services.EssentialsConditionProvider::class.java) + val conditionUri = com.sameerasw.essentials.services.EssentialsConditionProvider.CONDITION_URI + + val ruleBuilder = android.app.AutomaticZenRule.Builder("Essentials Focus", conditionUri) + .setOwner(componentName) + .setDeviceEffects(effects) + .setInterruptionFilter(android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(android.service.notification.ZenPolicy.Builder().allowAlarms(true).build()) + .setConditionId(conditionUri) + .setConfigurationActivity(android.content.ComponentName(context, com.sameerasw.essentials.MainActivity::class.java)) + + if (ruleKey != null) { + nm.updateAutomaticZenRule(ruleKey, ruleBuilder.build()) + } else { + nm.addAutomaticZenRule(ruleBuilder.build()) + } + + // Trigger the condition to be TRUE + com.sameerasw.essentials.services.EssentialsConditionProvider.setConditionState(context, true) + + android.util.Log.d("DeviceEffects", "Updated ZenRule for Device Effects") + + } else { + // DISABLE EFFECTS + val existingRuleEntry = nm.automaticZenRules.entries.find { it.value.name == "Essentials Focus" } + existingRuleEntry?.let { entry -> + val rule = entry.value + rule.isEnabled = false + nm.updateAutomaticZenRule(entry.key, rule) + } + // Also notify condition false just in case + com.sameerasw.essentials.services.EssentialsConditionProvider.setConditionState(context, false) + + android.util.Log.d("DeviceEffects", "Disabled ZenRule for Device Effects") + } + + } catch (e: Exception) { + e.printStackTrace() + } + } + } + } } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/activities/AutomationEditorActivity.kt b/app/src/main/java/com/sameerasw/essentials/ui/activities/AutomationEditorActivity.kt index 926f8b59..23f431d5 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/activities/AutomationEditorActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/activities/AutomationEditorActivity.kt @@ -212,7 +212,8 @@ class AutomationEditorActivity : ComponentActivity() { // Config Sheets var showDimSettings by remember { mutableStateOf(false) } - var configAction by remember { mutableStateOf(null) } + var showDeviceEffectsSettings by remember { mutableStateOf(false) } + var configAction by remember { mutableStateOf(null) } // Generic config action // Validation val isValid = when (automationType) { @@ -247,7 +248,9 @@ class AutomationEditorActivity : ComponentActivity() { text = { Text(stringResource(R.string.action_delete)) }, onClick = { showMenu = false - DIYRepository.removeAutomation(existingAutomation.id) + if (existingAutomation != null) { + DIYRepository.removeAutomation(existingAutomation.id) + } finish() }, leadingIcon = { @@ -305,7 +308,7 @@ class AutomationEditorActivity : ComponentActivity() { Box( modifier = Modifier .fillMaxSize() - .maskClip(MaterialTheme.shapes.extraLarge) + .clip(MaterialTheme.shapes.extraLarge) .background(MaterialTheme.colorScheme.surfaceContainerHigh) ) { if (index == 0) { @@ -493,13 +496,16 @@ class AutomationEditorActivity : ComponentActivity() { } RoundedCardContainer(spacing = 2.dp) { - val actions = listOf( + val actions = mutableListOf( Action.TurnOnFlashlight, Action.TurnOffFlashlight, Action.ToggleFlashlight, Action.HapticVibration, Action.DimWallpaper() ) + // Only show Device Effects on Android 15+ + actions.add(Action.DeviceEffects()) + val currentSelection = when(automationType) { Automation.Type.TRIGGER -> selectedAction @@ -525,7 +531,6 @@ class AutomationEditorActivity : ComponentActivity() { actions.forEach { action -> // Check if the current selection matches this action type and update 'action' with the selected values if so - // This ensures the loop variable 'action' doesn't overwrite the configured values in 'currentSelection' val resolvedAction = if (currentSelection != null && currentSelection::class == action::class) currentSelection else action EditorActionItem( @@ -541,11 +546,22 @@ class AutomationEditorActivity : ComponentActivity() { else selectedOutAction = resolvedAction } } + // Check permissions immediately on selection + // For Device Effects, we need Notification Policy Access + if (resolvedAction is Action.DeviceEffects) { + val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager + if (!nm.isNotificationPolicyAccessGranted) { + val intent = Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS) + context.startActivity(intent) + } + } }, onSettingsClick = { + configAction = resolvedAction if (resolvedAction is Action.DimWallpaper) { - configAction = resolvedAction showDimSettings = true + } else if (resolvedAction is Action.DeviceEffects) { + showDeviceEffectsSettings = true } } ) @@ -556,9 +572,9 @@ class AutomationEditorActivity : ComponentActivity() { } } - if (showDimSettings && configAction != null) { + if (showDimSettings && configAction is Action.DimWallpaper) { DimWallpaperSettingsSheet( - initialAction = configAction!!, + initialAction = configAction as Action.DimWallpaper, onDismiss = { showDimSettings = false }, onSave = { newAction -> showDimSettings = false @@ -575,6 +591,24 @@ class AutomationEditorActivity : ComponentActivity() { ) } + if (showDeviceEffectsSettings && configAction is Action.DeviceEffects) { + com.sameerasw.essentials.ui.components.sheets.DeviceEffectsSettingsSheet( + initialAction = configAction as Action.DeviceEffects, + onDismiss = { showDeviceEffectsSettings = false }, + onSave = { newAction -> + showDeviceEffectsSettings = false + when(automationType) { + Automation.Type.TRIGGER -> selectedAction = newAction + Automation.Type.STATE, Automation.Type.APP -> { + if (selectedActionTab == 0) selectedInAction = newAction + else selectedOutAction = newAction + } + } + configAction = null + } + ) + } + // Bottom Actions Row( modifier = Modifier diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/DeviceEffectsSettingsSheet.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/DeviceEffectsSettingsSheet.kt new file mode 100644 index 00000000..89c6e332 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/DeviceEffectsSettingsSheet.kt @@ -0,0 +1,307 @@ +package com.sameerasw.essentials.ui.components.sheets + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Checkbox +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.sameerasw.essentials.R +import com.sameerasw.essentials.domain.diy.Action +import com.sameerasw.essentials.utils.ColorUtil +import com.sameerasw.essentials.utils.HapticUtil + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun DeviceEffectsSettingsSheet( + initialAction: Action.DeviceEffects, + onDismiss: () -> Unit, + onSave: (Action.DeviceEffects) -> Unit +) { + val view = LocalView.current + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + + var enabled by remember { mutableStateOf(initialAction.enabled) } + var grayscale by remember { mutableStateOf(initialAction.grayscale) } + var suppressAmbient by remember { mutableStateOf(initialAction.suppressAmbient) } + var dimWallpaper by remember { mutableStateOf(initialAction.dimWallpaper) } + var nightMode by remember { mutableStateOf(initialAction.nightMode) } + + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, + containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, + dragHandle = null + ) { + Column( + modifier = Modifier + .padding(24.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + Text( + text = stringResource(R.string.diy_action_device_effects), + style = MaterialTheme.typography.headlineSmall, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface + ) + + // Info Card + Card( + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceBright + ), + shape = RoundedCornerShape(24.dp) + ) { + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + val infoTitle = "Info" // Key for color generation + Box( + modifier = Modifier + .size(40.dp) + .background( + color = ColorUtil.getPastelColorFor(infoTitle), + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(R.drawable.rounded_info_24), + contentDescription = null, + tint = ColorUtil.getVibrantColorFor(infoTitle), + modifier = Modifier.size(24.dp) + ) + } + Column { + Text( + text = stringResource(R.string.diy_device_effects_desc), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + if (android.os.Build.VERSION.SDK_INT < 35) { + Spacer(modifier = Modifier.size(8.dp)) + Text( + text = stringResource(R.string.diy_device_effects_android_15_warning), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.error, + fontWeight = FontWeight.Bold + ) + } + } + } + } + + // Settings Container + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + // Master Toggle (Enable/Disable) + Card( + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceBright + ), + shape = RoundedCornerShape(24.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + HapticUtil.performVirtualKeyHaptic(view) + enabled = !enabled + } + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + val toggleTitle = stringResource(R.string.diy_device_effects_enabled) + Box( + modifier = Modifier + .size(40.dp) + .background( + color = if (enabled) ColorUtil.getPastelColorFor(toggleTitle) else MaterialTheme.colorScheme.surfaceContainerHighest, + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(id = if (enabled) R.drawable.rounded_check_circle_24 else R.drawable.rounded_cancel_24), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = if (enabled) ColorUtil.getVibrantColorFor(toggleTitle) else MaterialTheme.colorScheme.onSurfaceVariant + ) + } + + Text( + text = stringResource(if (enabled) R.string.diy_device_effects_enabled else R.string.diy_device_effects_disabled), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.weight(1f), + color = MaterialTheme.colorScheme.onSurface + ) + Switch( + checked = enabled, + onCheckedChange = { + HapticUtil.performVirtualKeyHaptic(view) + enabled = it + } + ) + } + } + + if (enabled) { + // Effect Toggles in a Card + Card( + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceBright + ), + shape = RoundedCornerShape(24.dp) + ) { + Column( + verticalArrangement = Arrangement.spacedBy(0.dp) + ) { + EffectToggleItem( + title = stringResource(R.string.diy_effect_grayscale), + isChecked = grayscale, + onCheckedChange = { grayscale = it } + ) + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f), modifier = Modifier.padding(horizontal = 16.dp)) + EffectToggleItem( + title = stringResource(R.string.diy_effect_suppress_ambient), + isChecked = suppressAmbient, + onCheckedChange = { suppressAmbient = it } + ) + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f), modifier = Modifier.padding(horizontal = 16.dp)) + EffectToggleItem( + title = stringResource(R.string.diy_effect_dim_wallpaper), + isChecked = dimWallpaper, + onCheckedChange = { dimWallpaper = it } + ) + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f), modifier = Modifier.padding(horizontal = 16.dp)) + EffectToggleItem( + title = stringResource(R.string.diy_effect_night_mode), + isChecked = nightMode, + onCheckedChange = { nightMode = it } + ) + } + } + } + } + + // Buttons + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Button( + onClick = { + HapticUtil.performVirtualKeyHaptic(view) + onDismiss() + }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.surfaceBright, + contentColor = MaterialTheme.colorScheme.onSurface + ) + ) { + Icon( + painter = painterResource(id = R.drawable.rounded_close_24), + contentDescription = null, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.size(8.dp)) + Text(stringResource(R.string.action_cancel)) + } + + Button( + onClick = { + HapticUtil.performVirtualKeyHaptic(view) + onSave(initialAction.copy( + enabled = enabled, + grayscale = grayscale, + suppressAmbient = suppressAmbient, + dimWallpaper = dimWallpaper, + nightMode = nightMode + )) + }, + enabled = android.os.Build.VERSION.SDK_INT >= 35, + modifier = Modifier.weight(1f) + ) { + Icon( + painter = painterResource(id = R.drawable.rounded_check_24), + contentDescription = null, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.size(8.dp)) + Text(stringResource(R.string.action_save)) + } + } + } + } +} + +@Composable +private fun EffectToggleItem( + title: String, + isChecked: Boolean, + onCheckedChange: (Boolean) -> Unit +) { + val view = LocalView.current + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + HapticUtil.performVirtualKeyHaptic(view) + onCheckedChange(!isChecked) + } + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Checkbox( + checked = isChecked, + onCheckedChange = { + HapticUtil.performVirtualKeyHaptic(view) + onCheckedChange(it) + } + ) + Text( + text = title, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.weight(1f) + ) + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/DimWallpaperSettingsSheet.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/DimWallpaperSettingsSheet.kt index 11bba8cd..11725832 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/DimWallpaperSettingsSheet.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/DimWallpaperSettingsSheet.kt @@ -2,14 +2,19 @@ package com.sameerasw.essentials.ui.components.sheets import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -31,7 +36,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.sameerasw.essentials.R import com.sameerasw.essentials.domain.diy.Action -import com.sameerasw.essentials.ui.components.containers.RoundedCardContainer +import com.sameerasw.essentials.utils.ColorUtil import com.sameerasw.essentials.utils.HapticUtil @OptIn(ExperimentalMaterial3Api::class) @@ -65,62 +70,110 @@ fun DimWallpaperSettingsSheet( color = MaterialTheme.colorScheme.onSurface ) - // Permissions Info - RoundedCardContainer { - Row( - modifier = Modifier.padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - Icon( - painter = painterResource(R.drawable.rounded_info_24), - contentDescription = null, - tint = MaterialTheme.colorScheme.primary - ) - Text( - text = stringResource(R.string.diy_dim_wallpaper_desc), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - - // Permission Icons - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 12.dp), - horizontalArrangement = Arrangement.spacedBy(16.dp) - ) { - // Shizuku Icon - Icon( - painter = painterResource(R.drawable.rounded_adb_24), - contentDescription = "Shizuku", - tint = MaterialTheme.colorScheme.onSurfaceVariant - ) - // Root Icon - Icon( - painter = painterResource(R.drawable.rounded_numbers_24), - contentDescription = "Root", - tint = MaterialTheme.colorScheme.onSurfaceVariant - ) + // Permissions Info with Pastel Icon + Card( + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceBright + ), + shape = RoundedCornerShape(24.dp) + ) { + Column { + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + val infoTitle = "Dim Wallpaper" + Box( + modifier = Modifier + .size(40.dp) + .background( + color = ColorUtil.getPastelColorFor(infoTitle), + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(R.drawable.rounded_info_24), + contentDescription = null, + tint = ColorUtil.getVibrantColorFor(infoTitle), + modifier = Modifier.size(24.dp) + ) + } + Text( + text = stringResource(R.string.diy_dim_wallpaper_desc), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + + // Permission Icons + Row( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp, start = 16.dp, end = 16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally) + ) { + // Shizuku Icon + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Icon( + painter = painterResource(R.drawable.rounded_adb_24), + contentDescription = "Shizuku", + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(20.dp) + ) + Text("Shizuku", style = MaterialTheme.typography.labelMedium) + } + + // Root Icon + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Icon( + painter = painterResource(R.drawable.rounded_numbers_24), + contentDescription = "Root", + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(20.dp) + ) + Text("Root", style = MaterialTheme.typography.labelMedium) + } + } } } - // Slider - Column { - Text( - text = "Dim Amount: ${(dimAmount * 100).toInt()}%", - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onSurface - ) - Slider( - value = dimAmount, - onValueChange = { - dimAmount = it - HapticUtil.performSliderHaptic(view) - }, - valueRange = 0f..1f - ) + // Slider Card + Card( + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceBright + ), + shape = RoundedCornerShape(24.dp) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Dim Amount", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = "${(dimAmount * 100).toInt()}%", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.primary + ) + } + + Slider( + value = dimAmount, + onValueChange = { + dimAmount = it + HapticUtil.performSliderHaptic(view) + }, + valueRange = 0f..1f, + modifier = Modifier.padding(top = 8.dp) + ) + } } // Buttons diff --git a/app/src/main/res/drawable/round_king_bed_24.xml b/app/src/main/res/drawable/round_king_bed_24.xml new file mode 100644 index 00000000..601a6ddc --- /dev/null +++ b/app/src/main/res/drawable/round_king_bed_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_bed_24.xml b/app/src/main/res/drawable/rounded_bed_24.xml new file mode 100644 index 00000000..8260d43e --- /dev/null +++ b/app/src/main/res/drawable/rounded_bed_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_cancel_24.xml b/app/src/main/res/drawable/rounded_cancel_24.xml new file mode 100644 index 00000000..8d26e4fe --- /dev/null +++ b/app/src/main/res/drawable/rounded_cancel_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_check_circle_24.xml b/app/src/main/res/drawable/rounded_check_circle_24.xml new file mode 100644 index 00000000..a426909f --- /dev/null +++ b/app/src/main/res/drawable/rounded_check_circle_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bad5805e..25cc7fe2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -509,6 +509,17 @@ Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled + Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From d92287284ee46bb10d819de83fba01e4cdcad7b3 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Sat, 17 Jan 2026 19:14:26 +0530 Subject: [PATCH 016/254] Revert "fix: harmonize warning color in battery widget" This reverts commit 47c79dc6effc46298c1ce3632300381f48b5f940. --- .gitignore | 3 +-- .../sameerasw/essentials/services/widgets/BatteriesWidget.kt | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 2cbec1e6..366ca372 100644 --- a/.gitignore +++ b/.gitignore @@ -30,5 +30,4 @@ replay_pid* *.DS_Store *app/release/ -Build/ -build/reports/problems/problems-report.html +Build/ \ No newline at end of file diff --git a/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt b/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt index 1d281be3..671f5e02 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt @@ -25,7 +25,6 @@ import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils import androidx.glance.Image import androidx.glance.ImageProvider -import com.google.android.material.color.MaterialColors import com.sameerasw.essentials.R class BatteriesWidget : GlanceAppWidget() { @@ -156,7 +155,6 @@ class BatteriesWidget : GlanceAppWidget() { val baseError = GlanceTheme.colors.error.getColor(configContext).toArgb() val onSurface = GlanceTheme.colors.onSurface.getColor(configContext).toArgb() val surfaceColor = GlanceTheme.colors.surface.getColor(configContext).toArgb() - val warning = MaterialColors.harmonizeWithPrimary(configContext, android.graphics.Color.parseColor("#FFC107")) val isNightMode = (systemConfig.uiMode and android.content.res.Configuration.UI_MODE_NIGHT_MASK) == @@ -166,7 +164,7 @@ class BatteriesWidget : GlanceAppWidget() { val colors = ThemeColors( primary = basePrimary, error = baseError, - warning = warning, + warning = android.graphics.Color.parseColor("#FFC107"), track = ColorUtils.setAlphaComponent(onSurface, 30), surface = surfaceColor, iconTint = onSurface From 85755ccaec27468c103ab5f8399c30e3586d2cfe Mon Sep 17 00:00:00 2001 From: sameerasw Date: Sat, 17 Jan 2026 19:14:58 +0530 Subject: [PATCH 017/254] minor fix --- .../sameerasw/essentials/services/widgets/BatteriesWidget.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt b/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt index 671f5e02..a4c818ea 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt @@ -26,6 +26,7 @@ import androidx.core.graphics.ColorUtils import androidx.glance.Image import androidx.glance.ImageProvider import com.sameerasw.essentials.R +import androidx.core.graphics.toColorInt class BatteriesWidget : GlanceAppWidget() { override val sizeMode = androidx.glance.appwidget.SizeMode.Exact @@ -164,7 +165,7 @@ class BatteriesWidget : GlanceAppWidget() { val colors = ThemeColors( primary = basePrimary, error = baseError, - warning = android.graphics.Color.parseColor("#FFC107"), + warning = "#FFC107".toColorInt(), track = ColorUtils.setAlphaComponent(onSurface, 30), surface = surfaceColor, iconTint = onSurface From 60a9633cdd6f0a69805f32ede87a787870a0b058 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Sat, 17 Jan 2026 19:34:26 +0530 Subject: [PATCH 018/254] Update widget background --- .../sameerasw/essentials/services/widgets/BatteriesWidget.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt b/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt index a4c818ea..df3d505f 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/widgets/BatteriesWidget.kt @@ -155,7 +155,7 @@ class BatteriesWidget : GlanceAppWidget() { val basePrimary = GlanceTheme.colors.primary.getColor(configContext).toArgb() val baseError = GlanceTheme.colors.error.getColor(configContext).toArgb() val onSurface = GlanceTheme.colors.onSurface.getColor(configContext).toArgb() - val surfaceColor = GlanceTheme.colors.surface.getColor(configContext).toArgb() + val widgetBackgroundColor = GlanceTheme.colors.widgetBackground.getColor(configContext).toArgb() val isNightMode = (systemConfig.uiMode and android.content.res.Configuration.UI_MODE_NIGHT_MASK) == @@ -167,7 +167,7 @@ class BatteriesWidget : GlanceAppWidget() { error = baseError, warning = "#FFC107".toColorInt(), track = ColorUtils.setAlphaComponent(onSurface, 30), - surface = surfaceColor, + surface = widgetBackgroundColor, iconTint = onSurface ) From 6816a1b809d727e91a15bcbcf3c3fc1dc9675e98 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Sat, 17 Jan 2026 20:10:45 +0530 Subject: [PATCH 019/254] Attempted fix for #106 root permission spam --- .../com/sameerasw/essentials/utils/RootUtils.kt | 5 +---- .../essentials/viewmodels/MainViewModel.kt | 13 +++++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/utils/RootUtils.kt b/app/src/main/java/com/sameerasw/essentials/utils/RootUtils.kt index 5ea5bd79..94c13c3d 100644 --- a/app/src/main/java/com/sameerasw/essentials/utils/RootUtils.kt +++ b/app/src/main/java/com/sameerasw/essentials/utils/RootUtils.kt @@ -7,10 +7,7 @@ object RootUtils { fun isRootAvailable(): Boolean { return try { - val process = Runtime.getRuntime().exec("su") - val os = DataOutputStream(process.outputStream) - os.writeBytes("exit\n") - os.flush() + val process = Runtime.getRuntime().exec(arrayOf("sh", "-c", "which su")) val exitCode = process.waitFor() exitCode == 0 } catch (e: Exception) { diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt index b6a05e12..a77a96c9 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt @@ -235,10 +235,6 @@ class MainViewModel : ViewModel() { isBluetoothPermissionGranted.value = PermissionUtils.hasBluetoothPermission(context) - isRootAvailable.value = com.sameerasw.essentials.utils.RootUtils.isRootAvailable() - isRootPermissionGranted.value = com.sameerasw.essentials.utils.RootUtils.isRootPermissionGranted() - - settingsRepository.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener) settingsRepository.registerOnSharedPreferenceChangeListener(preferenceChangeListener) isWidgetEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_WIDGET_ENABLED) @@ -259,6 +255,15 @@ class MainViewModel : ViewModel() { notificationLightingIndicatorX.value = settingsRepository.getFloat(SettingsRepository.KEY_EDGE_LIGHTING_INDICATOR_X, 50f) notificationLightingIndicatorY.value = settingsRepository.getFloat(SettingsRepository.KEY_EDGE_LIGHTING_INDICATOR_Y, 2f) isRootEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_USE_ROOT) + + if (isRootEnabled.value) { + isRootAvailable.value = com.sameerasw.essentials.utils.RootUtils.isRootAvailable() + isRootPermissionGranted.value = com.sameerasw.essentials.utils.RootUtils.isRootPermissionGranted() + } else { + isRootAvailable.value = false + isRootPermissionGranted.value = false + } + notificationLightingIndicatorScale.value = settingsRepository.getFloat(SettingsRepository.KEY_EDGE_LIGHTING_INDICATOR_SCALE, 1.0f) notificationLightingGlowSides.value = settingsRepository.getNotificationLightingGlowSides() From a1bb4f331f306eb7aff00b89f45f95c2f7dc2ef7 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:00 +0530 Subject: [PATCH 020/254] Update source file strings.xml --- app/src/main/res/values/strings.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a23d06cd..25cc7fe2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -492,6 +492,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -507,6 +509,17 @@ Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled + Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From a4fac47911abdbb02602d68856a460507f41d640 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:03 +0530 Subject: [PATCH 021/254] New translations strings.xml (Romanian) --- app/src/main/res/values-ro/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 8d28e05a..6d8c5d91 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 2747443853dff34d2acb711f72a22679ad6c0da1 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:05 +0530 Subject: [PATCH 022/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2f32cf6d..0e30e5ca 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Sélectionner un déclencheur + App + Automate based on open app État de sélection Action de sélection In Action @@ -476,6 +478,16 @@ Service d\'automatisation Automatisations actives Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna L\'outil tout-en-un pour votre Pixel ou vos appareils Android From aa1cacec01d9c9bfdcbb3a6bb9293b9aaf1f9b23 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:06 +0530 Subject: [PATCH 023/254] New translations strings.xml (Spanish) --- app/src/main/res/values-es/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 3265f8ae076b81cc3685c5acfa5c545a700d8b0f Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:07 +0530 Subject: [PATCH 024/254] New translations strings.xml (Afrikaans) --- app/src/main/res/values-af/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From d3e47b640cb36620cc39ba9f065f274412d738a8 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:08 +0530 Subject: [PATCH 025/254] New translations strings.xml (Arabic) --- app/src/main/res/values-ar/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 352099e12d19ea01f4c803e9a3922824f60c54f2 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:09 +0530 Subject: [PATCH 026/254] New translations strings.xml (Catalan) --- app/src/main/res/values-ca/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 8ca6a53e98f56411e6b0d258f3f91f8082aff03b Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:10 +0530 Subject: [PATCH 027/254] New translations strings.xml (Czech) --- app/src/main/res/values-cs/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 4a754e133c4b315b182816c265bf87a7f6695b71 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:11 +0530 Subject: [PATCH 028/254] New translations strings.xml (Danish) --- app/src/main/res/values-da/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From e82ed92bb9c9e9d4c2b0fc21b151888947cf1f66 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:12 +0530 Subject: [PATCH 029/254] New translations strings.xml (German) --- app/src/main/res/values-de/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 9dc8db88..3ee08fca 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -463,6 +463,8 @@ Hintergrund dimmen Diese Aktion erfordert Shizuku oder einen Root-Zugang um den Hintergrund zu dimmen. Auslöser auswählen + App + Automate based on open app Zustand auswählen Aktion auswählen Eintrittsaktion @@ -476,6 +478,16 @@ Automatisierungsdienst Automatisierungen aktiv Überwachung von Systemereignissen für deine Automatisierungen + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna Die alles umfassende Werkzeugkiste für Ihr Pixel und Android-Geräte From ff6403a0eddb01d192e7859564599edb0d1e9e89 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:13 +0530 Subject: [PATCH 030/254] New translations strings.xml (Greek) --- app/src/main/res/values-el/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 69166aadafe46c6ed7579e6c15563a6e52a4abd2 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:14 +0530 Subject: [PATCH 031/254] New translations strings.xml (Finnish) --- app/src/main/res/values-fi/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 429dba04e3e96a6303846acf30656c381cd9a525 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:15 +0530 Subject: [PATCH 032/254] New translations strings.xml (Hebrew) --- app/src/main/res/values-he/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 1702e1f315b8435f91ce11166a4788c24bdc1273 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:17 +0530 Subject: [PATCH 033/254] New translations strings.xml (Hungarian) --- app/src/main/res/values-hu/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 8037d1af5d1ae865ec2e48a581a62feadfd91345 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:17 +0530 Subject: [PATCH 034/254] New translations strings.xml (Italian) --- app/src/main/res/values-it/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 11496709..6868b6ac 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -464,6 +464,8 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -477,6 +479,16 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 861745bad2d07e9df5d5732142111b256ff0c2a5 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:18 +0530 Subject: [PATCH 035/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 18dd74cb..ad9d2ae4 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -463,6 +463,8 @@ 暗い壁紙 このアクションでは、壁紙を暗くするために、ShizukuまたはRootが必要です。 トリガーを選択 + App + Automate based on open app ステータスを選択 アクションを選択 条件と合うとき @@ -476,6 +478,16 @@ オートメーションサービス オートメーション動作中 自動化システムイベントを監視中です + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna あなたのPixelとAndroidのオールインワンツールボックス From 596f07f4eb0a610585bc911600589078e110e3e8 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:20 +0530 Subject: [PATCH 036/254] New translations strings.xml (Korean) --- app/src/main/res/values-ko/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From bcd3fd4b20ce4838c2272e120cc025385ee8af97 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:21 +0530 Subject: [PATCH 037/254] New translations strings.xml (Dutch) --- app/src/main/res/values-nl/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 5db9cf130d3c3efc829707950c04033d2538d5c7 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:22 +0530 Subject: [PATCH 038/254] New translations strings.xml (Norwegian) --- app/src/main/res/values-no/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From df50f04f039007e39ab963cba41892e6b012f0a1 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:23 +0530 Subject: [PATCH 039/254] New translations strings.xml (Polish) --- app/src/main/res/values-pl/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 3a480200a618947e51b798b0eb3715b52458ae4e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:24 +0530 Subject: [PATCH 040/254] New translations strings.xml (Portuguese) --- app/src/main/res/values-pt/strings.xml | 30 ++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 2828ec5d..7b2cd732 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -379,7 +379,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids @@ -750,7 +762,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -769,14 +781,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -801,8 +813,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining (%2$d%%) + Travel alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From 55ac58da9165b5a8a366f1f1a421a71eaf0a3fc8 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:25 +0530 Subject: [PATCH 041/254] New translations strings.xml (Russian) --- app/src/main/res/values-ru/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 349fe98b..679e7f5a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 29c7704ea385d4765b94db0b685da96358935f5d Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:26 +0530 Subject: [PATCH 042/254] New translations strings.xml (Serbian (Cyrillic)) --- app/src/main/res/values-sr/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 300e74c849bddc56c21ea9d42b9bc213d0709745 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:27 +0530 Subject: [PATCH 043/254] New translations strings.xml (Swedish) --- app/src/main/res/values-sv/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 1c9694904e4eedc242aada3fa22c43ae582d79a7 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:28 +0530 Subject: [PATCH 044/254] New translations strings.xml (Turkish) --- app/src/main/res/values-tr/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 914acf34494ae5f9ca0f3f774aa965c924f2fe88 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:29 +0530 Subject: [PATCH 045/254] New translations strings.xml (Ukrainian) --- app/src/main/res/values-uk/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 5a35dab54dbbc573c27991aadc909a4aba2439b4 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:30 +0530 Subject: [PATCH 046/254] New translations strings.xml (Chinese Simplified) --- app/src/main/res/values-zh/strings.xml | 30 ++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 2828ec5d..7b2cd732 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -379,7 +379,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids @@ -750,7 +762,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -769,14 +781,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -801,8 +813,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining (%2$d%%) + Travel alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From b9786a65ebdfac60c1f6c69fc1da9683bdb1de1a Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:31 +0530 Subject: [PATCH 047/254] New translations strings.xml (Chinese Traditional) --- app/src/main/res/values-zh/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 7b2cd732..423e8d42 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -379,7 +379,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -762,7 +762,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -781,14 +781,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -813,8 +813,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining + Travel Alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From aecd1665449d285d910695d543864ab2bab106ad Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:32 +0530 Subject: [PATCH 048/254] New translations strings.xml (English) --- app/src/main/res/values-en/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 7949d700a3be7f3e7d5ff89b3fca5b2de03dc9db Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:33 +0530 Subject: [PATCH 049/254] New translations strings.xml (Vietnamese) --- app/src/main/res/values-vi/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 974c76383470ed373073b43fd33cae9339f1bf3d Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:34 +0530 Subject: [PATCH 050/254] New translations strings.xml (Portuguese, Brazilian) --- app/src/main/res/values-pt/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 7b2cd732..423e8d42 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -379,7 +379,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -762,7 +762,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -781,14 +781,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -813,8 +813,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining + Travel Alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From ee01e87cd6df9ab28a41422e042438233bb7e021 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:35 +0530 Subject: [PATCH 051/254] New translations strings.xml (Sinhala) --- app/src/main/res/values-si/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From fd28f9389561cfd824e9a99519c699fc473d7e11 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sat, 17 Jan 2026 20:25:36 +0530 Subject: [PATCH 052/254] New translations strings.xml (Acholi) --- app/src/main/res/values-ach/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-ach/strings.xml b/app/src/main/res/values-ach/strings.xml index 2828ec5d..423e8d42 100644 --- a/app/src/main/res/values-ach/strings.xml +++ b/app/src/main/res/values-ach/strings.xml @@ -463,6 +463,8 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Select Trigger + App + Automate based on open app Select State Select Action In Action @@ -476,6 +478,16 @@ Automation Service Automations Active Monitoring system events for your automations + Device Effects + Disable Device Effects + Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. + Grayscale + Suppress Ambient Display + Dim Wallpaper + Night Mode + This feature requires Android 15 or higher. + Enabled + Disabled Sameera Wijerathna The all-in-one toolbox for your Pixel and Androids From 78b9a1135fa26c6ec873c9e3664b1e249535c24b Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sun, 18 Jan 2026 06:54:29 +0530 Subject: [PATCH 053/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 202 ++++++++++++------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0e30e5ca..a1fb8ac8 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -11,7 +11,7 @@ Application gelée Widget de verrouillage invisible Gel d\'applis - Flashlight Pulse + Impulsion de lampe torche Vérifier pour des pré-versions Peut être instable @@ -36,7 +36,7 @@ Lampe-torche Options de la lampe-torche Adjust fading and other settings - Pitch black theme + Thème Noir Absolu Use pure black background in dark mode Retour haptique Réattribuer l\'appui long @@ -73,22 +73,22 @@ Envoyer des notifications Autorise l\'application à afficher des notifications Accorder la permission - Caffeinate Active - Active + Caffeinate activé + Actif Screen is being kept awake - Ignore battery optimization + Ignorer l\'optimisation de la batterie Recommended for reliable service on some devices Abort with screen off - Timeout Presets + Préréglages de délais Select available durations for QS tile 5m 10m 30m 1h - Starting in %1$ds… - %1$s remaining - Persistent notification for Caffeinate + Démarrage dans %1$ds… + %1$s RESTANT + Notification persistante pour Caffeinate Activer le mode d\'Éclairage nocturne dynamique Applis qui désactivent le mode d\'Éclairage nocturne @@ -128,12 +128,12 @@ Stroke adjustment Rayon des coins Stroke thickness - Glow adjustment - Glow spread + Ajustement de la brillance + Propagation de la brillance Placement Position horizontale Position verticale - Indicator adjustment + Ajustement de l\'indicateur Taille Durée Animation @@ -145,7 +145,7 @@ Convient si vous n\'utilisez pas le Always-On Display. Réveille l\'écran et affiche de la lumière Affiche l\'écran de verrouillage - No black overlay + Pas de superposition noire Ajouter Déjà ajouté @@ -164,10 +164,10 @@ Audio mono Lampe-torche Gel d\'applis - Flashlight Pulse + Impulsion de lampe torche Rester allumé - Essentials Keyboard - English (US) + Clavier Essentials + Anglais (US) Actif Inactif NFC @@ -191,8 +191,8 @@ Silencieux Connectivité - Phone & Network - Audio & Media + Téléphone & Réseau + Audio & Média Status du système Spécifique au constructeur @@ -224,7 +224,7 @@ Synchronisation Profil géré Ne pas déranger - Privacy & Secure Folder + Confidentialité & Dossier sécurisé Status de sécurité (SU) Clavier / Souris OTG Fonctionnalités intelligentes Samsung @@ -264,22 +264,22 @@ Maintenir l\'écran allumé Mode économie d\'énergie Maps Pour tout appareil Android - Notification lighting - Light up for notifications + Éclairage de notifications + Éclairer pour les notifications Raccourci de mode de sonnerie Bloc \"Réglages rapides\" pour changer le mode de sonnerie Bloc \"rester allumé\" Bloc \"Réglages rapides\" pour activer/désactiver le maintien de l\'écran allumé - Show Bluetooth devices - Display battery level of connected Bluetooth devices + Afficher les appareils Bluetooth + Afficher le niveau de batterie des appareils Bluetooth connectés Limit max devices Adjust max devices visible in widget - Widget background - Show widget background + Fond du widget + Afficher le fond du widget Déclencher une automatisation Prévoir une action à déclencher lors d\'une observation - State Automation + Automatisation de l\'état Schedule an action to execute based on the state of a condition in and out Nouvelle automatisation Modifier l\'automatisation @@ -307,7 +307,7 @@ Données intelligentes Cache les données mobiles dans certains modes Réinitialiser les icônes - Reset status bar icon visibility to default + Réinitialiser la visibilité des icônes de la barre d\'état Abort Caffeinate with screen off Automatically turn off Caffeinate when manually locking the device Style d\'éclairage @@ -316,10 +316,10 @@ Adjust the corner radius of the notification lighting Ignorer les notifications silencieuses Ne pas afficher de lumière pour les notifications silencieuses - Flashlight pulse - Slowly pulse flashlight for new notifications - Only while facing down - Pulse flashlight only when device is face down + Impulsion de lampe torche + Faire clignoter lentement la lampe-torche pour les nouvelles notifications + Seulement quand le téléphone est vers le bas + Impulsion de lampe torche seulement quand l\'appareil est vers le bas Désactiver les notifications de débogage Cacher les notifications de débogage ADB/USB persistantes. Désactiver la notification de transfert de fichiers @@ -339,8 +339,8 @@ Caffeinate Basculer le maintient de l\'écran allumé Mode de sonnerie - Cycle sound modes (Ring/Vibrate/Silent) - Notification Lighting + Changer le mode de sonnerie (Sonnerie/Vibreur/Silencieux) + Éclairage de notifications Toggle notification lighting service Éclairage nocturne dynamique Night light automation toggle @@ -352,7 +352,7 @@ Dedicated flashlight toggle Gel d\'applis Lancer la grille de gel d\'applis - Flashlight Pulse + Impulsion de lampe torche Toggle notification flashlight pulse Toggle stay awake developer option Activer la réattribution de boutons @@ -383,17 +383,17 @@ Required to run power-saving commands while maps is navigating. Accès racine (root) Permissions required for system actions using Root privileges. - Notification Listener - Required to detect when Maps is navigating. - Required to detect new notifications - Required to detect and snooze notifications + Analyseur de notification + Requise pour détecter quand Maps est en navigation + Requise pour détecter les nouvelles notifications + Requise pour détecter et mettre en attente les notifications Service d\'accessibilité Requis pour le verrouillage d\'applis, le widget de verrouillage et d\'autres fonctionnalités pour détecter les interactions. Requis pour déclencher l\'éclairage lors de la réception d\'une nouvelle notification Requis pour intercepter les appuis sur les boutons physiques Requis pour surveiller les applications en avant-plan Modifier les paramètres de sécurité - Required for Statusbar icons and Screen Locked Security + Requise pour les icônes de la barre d\'état et la protection de l\'écran de verrouillage Needed to toggle Night Light. Grant via ADB or root. Modify System Settings Required to toggle Adaptive Brightness and other system settings @@ -406,7 +406,7 @@ Vérifier Activer dans les paramètres Comment accorder - Battery Optimization + Optimisation de la batterie Ensure the service is not killed by the system to save power. Essentials @@ -416,19 +416,19 @@ Applications désactivées Do It Yourself - Keyboard Setup - Enable in settings - Switch to Essentials - Keyboard is active and ready! - Enabled - Disabled - Adaptive Brightness - Toggle adaptive brightness - Maps Power Saving - Toggle maps power saving mode - Search for Tools, Mods and Tweaks + Configuration du clavier + Activer dans les paramètres + Changer vers Essentials + Le clavier est actif et prêt ! + Activé + Désactivé + Luminosité adaptative + Activer/désactiver la luminosité adaptative + Économie d\'énergie Maps + Activer/désactiver le mode économie d\'énergie Maps + Recherchez des outils, des mods, ou des améliorations Rechercher - Stop + Arrêter Rechercher Retour @@ -463,7 +463,7 @@ Dim Wallpaper This action requires Shizuku or Root to adjust system wallpaper dimming. Sélectionner un déclencheur - App + Appli Automate based on open app État de sélection Action de sélection @@ -478,16 +478,16 @@ Service d\'automatisation Automatisations actives Monitoring system events for your automations - Device Effects - Disable Device Effects + Effets d\'appareil + Désactiver les effets d\'appareil Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. Grayscale Suppress Ambient Display Dim Wallpaper - Night Mode - This feature requires Android 15 or higher. - Enabled - Disabled + Mode nuit + Cette fonctionnalité nécessite Android 15 ou supérieur. + Activé + Désactivé Sameera Wijerathna L\'outil tout-en-un pour votre Pixel ou vos appareils Android @@ -540,11 +540,11 @@ icône - keyboard - height + clavier + hauteur padding - haptic - input + haptique + entrée visible @@ -557,7 +557,7 @@ lumière lampe-torche - pulse + impulsion notification @@ -614,9 +614,9 @@ attendre timeout - Always dark theme - Pitch black theme - Clipboard History + Toujours le mode sombre + Thème Noir Absolu + Historique du presse-papiers liste sélecteur @@ -624,7 +624,7 @@ animation - visual + visuel look @@ -648,7 +648,7 @@ énergie - blur + flou glass vignette @@ -733,7 +733,7 @@ Zero Avatar du développeur - Aidr & Guides + Aide & Guides Besoin de plus d\'aide ? Contactez-nous, Réduire Étendre @@ -785,7 +785,7 @@ Soyez notifiés lorsque vous arrivez à une destination Destination Définir destination - Traçage : %1$.4f%2$.4f + Suivi : %1$.4f%2$.4f Aucune destination définie Ouvrez Google Maps, choisissez un endroit et partagez-le dans Essentials. Rayon : %d m @@ -799,7 +799,7 @@ Vous êtes arrivé à destination. Actuellement à %1$.1fkm Supprimer la destination - Processing location… + Traitement de la position... DISTANCE RESTANTE Calcul… Arrêter le traçage @@ -809,51 +809,51 @@ Supprimer Aucune destination Ouvrir Maps - Full-Screen Alarm Permission - Required to wake your device upon arrival. Tap to grant. + Permission d\'alarme en plein écran + Requise pour réveiller votre appareil à l\'arrivée. Appuyez pour autoriser %1$d m %1$.1f km - Travel Alarm active - %1$s remaining (%2$d%%) - Travel Progress - Shows real-time distance to destination + Alarme de trajet active + %1$s restant (%2$d%%) + Progrès du voyage : + Affiche la distance de la destination en temps réel Destination à proximité Prepare to get off - Dismiss - Destination set: %1$.4f, %2$.4f + Ignorer + Destination définie : %1$.4f, %2$.4f Utiliser l\'accès root (racine) À la place de Shizuku L\'accès racine (root) n\'est pas disponible. Merci de vérifier votre manager de root. - Keyboard - Keys + Clavier + Touches Customize layout and behavior - Keyboard Height + Hauteur du clavier Adjust the total vertical size of the keyboard Bottom Padding Add space below the keyboard - Haptic Feedback - Vibrate on key press - Test the keyboard - Test the keyboard - Keyboard Height + Retour haptique + Vibrer à l\'appui sur une touche + Tester le clavier + Tester le clavier + Hauteur du clavier Bottom Padding - Haptic Feedback + Retour haptique Key Roundness Key Roundness - Adjust the corner radius of keys - Move functions to bottom + Ajuster le rayon des coins des touches + Déplacer les fonctions en bas Functions side padding - Haptic feedback strength - Keyboard shape - Round - Flat + Force du retour haptique + Forme du clavier + Rond + Plat Inverse Batteries - Monitor your device battery levels - Battery Status - Connect to AirSync - Display battery from your connected mac device in AirSync - Download AirSync App - Required for Mac battery sync + Surveillez le niveau de batterie de vos appareils + Status de la batterie + Connecter à AirSync + Afficher la batterie de votre appareil Mac connecté dans AirSync + Télécharger l\'appli AirSync + Requis pour synchroniser la batterie d\'un Mac From 175dabd30266cf299cc083239367411a46d77838 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sun, 18 Jan 2026 07:55:16 +0530 Subject: [PATCH 054/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 82 +++++++++++++------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a1fb8ac8..219380a5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -35,9 +35,9 @@ Ouvrir Shizuku Lampe-torche Options de la lampe-torche - Adjust fading and other settings + Ajuster la disparition et les autres paramètres Thème Noir Absolu - Use pure black background in dark mode + Utiliser un fond noir pure en mode sombre Retour haptique Réattribuer l\'appui long Écran éteint @@ -57,7 +57,7 @@ Intensité de la lampe-torche Fade in and out Smoothly toggle flashlight - Global controls + Contrôles globlaux Fade-in flashlight globally Ajuster l\'intensité Les boutons Volume + et - ajustent l\'intensité de lampe-torche @@ -75,12 +75,12 @@ Accorder la permission Caffeinate activé Actif - Screen is being kept awake + L\'écran est maintenu allumé Ignorer l\'optimisation de la batterie - Recommended for reliable service on some devices - Abort with screen off + Recommandé pour un service fiable sur certains appareils + Arrêter au verrouillage de l\'écran Préréglages de délais - Select available durations for QS tile + Sélectionner les durées valables pour le bloc de \"Réglages rapides\" 5m 10m 30m @@ -121,8 +121,8 @@ Afficher seulement quand l\'écran est éteint Ignorer les notifications silencieuses Ignorer les notifications persistantes - Flashlight Pulse - Flashlight pulse + Impulsion de lampe torche + Impulsion de lampe torche Seulement quand le téléphone est vers le bas Style Stroke adjustment @@ -272,7 +272,7 @@ Bloc \"Réglages rapides\" pour activer/désactiver le maintien de l\'écran allumé Afficher les appareils Bluetooth Afficher le niveau de batterie des appareils Bluetooth connectés - Limit max devices + Nombre d\'appareils max Adjust max devices visible in widget Fond du widget Afficher le fond du widget @@ -308,7 +308,7 @@ Cache les données mobiles dans certains modes Réinitialiser les icônes Réinitialiser la visibilité des icônes de la barre d\'état - Abort Caffeinate with screen off + Arrêter Caffeinate au verrouillage Automatically turn off Caffeinate when manually locking the device Style d\'éclairage Choose between Stroke, Glow, Spinner, and more @@ -372,7 +372,7 @@ Geler toutes les applis Geler immédiatement toutes les applis choisies Geler au verrouillage - Freeze selected apps when device locks + Geler les applications sélectionnées quand l\'appareil est verrouillé Délai avant de geler Délai avant le gel après le verrouilage de l\'appareil @@ -380,9 +380,9 @@ Requis pour les commandes avancées. Installez Shizuku depuis le Play Store. Installer Shizuku Shizuku permission - Required to run power-saving commands while maps is navigating. + Requise pour exécuter des commandes d\'économie d\'énergie pendant la navigation de Maps. Accès racine (root) - Permissions required for system actions using Root privileges. + Permissions requises pour les actions systèmes en utilisant les privilèges racine (root) Analyseur de notification Requise pour détecter quand Maps est en navigation Requise pour détecter les nouvelles notifications @@ -394,20 +394,20 @@ Requis pour surveiller les applications en avant-plan Modifier les paramètres de sécurité Requise pour les icônes de la barre d\'état et la protection de l\'écran de verrouillage - Needed to toggle Night Light. Grant via ADB or root. - Modify System Settings - Required to toggle Adaptive Brightness and other system settings + Requise pour activer/désactiver l\'Éclairage nocturne. Autoriser via ADB ou root. + Modifier les paramètres système + Requise pour activer/désactiver la luminosité adaptative et d\'autres paramètres système Permission de superposition Required to display the notification lighting overlay on the screen Administrateur de l\'appareil - Required to hard-lock the device (disabling biometrics) on unauthorized access attempts + Requise pour faire un verrouillage forcé de l\'appareil (désactiver la biométrie) lors d\'accès non autorisés Accorder la permission Copier la commande ADB Vérifier Activer dans les paramètres Comment accorder Optimisation de la batterie - Ensure the service is not killed by the system to save power. + Assure que le service ne soit pas fermé par le système pour économise de l\'énergie. Essentials Gel d\'applis @@ -464,7 +464,7 @@ This action requires Shizuku or Root to adjust system wallpaper dimming. Sélectionner un déclencheur Appli - Automate based on open app + Automatisation basée sur l\'ouverture d\'appli État de sélection Action de sélection In Action @@ -477,12 +477,12 @@ Désactiver Service d\'automatisation Automatisations actives - Monitoring system events for your automations + Analyse des événements système pour vos automatisations Effets d\'appareil Désactiver les effets d\'appareil Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. Grayscale - Suppress Ambient Display + Enlever l\'éclairage ambient Dim Wallpaper Mode nuit Cette fonctionnalité nécessite Android 15 ou supérieur. @@ -566,20 +566,20 @@ allumé - awake + allumé développeur alimentation charge - glow + brillance notification led - round - shape - edge + rond + forme + coin secure @@ -594,13 +594,13 @@ hear - stay + rester on timeout - touch - wake + touché + réveiller affichage @@ -654,12 +654,12 @@ float - window - overlay + fenêtre + superposition toujours - display + affichage horloge @@ -741,10 +741,10 @@ E-mail Envoyer un e-mail Aucune application d\'e-mail disponible - Step %1$d Image + Image de l\'étape %1$d Permissions d\'accessibilité, de notification et de superposition - You may get this access denied message if you try to grant sensitive permissions such as accessibility, notification listener or overlay permissions. To grant it, check the steps below. + Vous pouvez obtenir ce message d\'accès refusé si vous essayer d\'accorder des permissions sensibles telles que l\'accessibilité, l\'analyse des notifications ou les permissions de superposition. Pour l\'accorder, regardez les étapes ci-dessous. 1. Allez dans la page d\'informations de l\'application Essentials 2. Ouvrez le menu à 3 points en haut à droite et sélectionnez \"Autoriser les paramètres restreints\". Vous devrez peut-être vous authentifier avec la biométrie. Une fois fait, réessayez d\'accorder la permission. Shizuku @@ -818,7 +818,7 @@ Progrès du voyage : Affiche la distance de la destination en temps réel Destination à proximité - Prepare to get off + Préparer à partir Ignorer Destination définie : %1$.4f, %2$.4f Utiliser l\'accès root (racine) @@ -827,11 +827,11 @@ Clavier Touches - Customize layout and behavior + Personnaliser l\'apparence et le comportement Hauteur du clavier - Adjust the total vertical size of the keyboard + Ajuster la taille verticale totale du clavier Bottom Padding - Add space below the keyboard + Ajouter de l\'espace en dessous du clavier Retour haptique Vibrer à l\'appui sur une touche Tester le clavier @@ -839,8 +839,8 @@ Hauteur du clavier Bottom Padding Retour haptique - Key Roundness - Key Roundness + Arrondi des touches + Arrondi des touches Ajuster le rayon des coins des touches Déplacer les fonctions en bas Functions side padding From 6235961019a50d84160c9e2edee156326a5fab2a Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sun, 18 Jan 2026 13:33:43 +0530 Subject: [PATCH 055/254] New translations strings.xml (Arabic) --- app/src/main/res/values-ar/strings.xml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 423e8d42..5b884936 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1,21 +1,21 @@ - Essentials - BETA + أساسيات + تجريبي Essentials Accessibility Service\n\nThis service is required for the following advanced features:\n\n• Physical Button Remapping:\nDetects volume button presses even when the screen is off to trigger actions like the Flashlight.\n\n• Per-App Settings:\nMonitors the currently active app to apply specific profiles for Dynamic Night Light, Notification Lighting Colors, and App Lock.\n\n• Screen Control:\nAllows the app to lock the screen (e.g. via Double Tap or Widgets) and detect screen state changes.\n\n• Security:\nPrevents unauthorized changes by detecting window content when the device is locked.\n\nNo input text or sensitive user data is collected or transmitted. - App icon - App Freezing - Disable apps that are rarely used - App Freezing - Open App Freezing - Frozen App + أيقونة التطبيق + تجميد التطبيق + تعطيل التطبيقات التي نادراً إستخدامها + تجميد التطبيق + تجميد التطبيق المفتوح + تطبيق مجمد Empty screen off widget - App Freezing - Flashlight Pulse + تجميد التطبيق + نبض المصباح Check for pre-releases Might be unstable - Security + الأمن Enable app lock App Lock Security Authenticate to enable app lock @@ -32,7 +32,7 @@ Shizuku is not running Detected %1$s Status: %1$s - Open Shizuku + فتح Shizuku Flashlight Flashlight options Adjust fading and other settings From d31b6c9c06685708120f282bc013a3bbda6be555 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sun, 18 Jan 2026 14:33:38 +0530 Subject: [PATCH 056/254] New translations strings.xml (Arabic) --- app/src/main/res/values-ar/strings.xml | 76 +++++++++++++------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 5b884936..fc91c91d 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -27,14 +27,14 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Works with screen off (Recommended) Shizuku is not running Detected %1$s Status: %1$s فتح Shizuku - Flashlight - Flashlight options + ضوء الكشاف + إعدادات ضوء الكشاف Adjust fading and other settings Pitch black theme Use pure black background in dark mode @@ -44,11 +44,11 @@ Screen On Volume Up Volume Down - Toggle flashlight + تشغيل ضوء الكشاف Media play/pause Media next Media previous - Toggle vibrate + تشغيل الإهتزاز Toggle mute AI assistant Take screenshot @@ -66,25 +66,25 @@ Other Always turn off flashlight Even while display is on - Settings + الإعدادات - Notification - Show Notification - Post Notifications - Allows the app to show notifications - Grant Permission - Caffeinate Active - Active - Screen is being kept awake + الإشعار + إظهار الإشعار + إشعارات المنشورات + يسمح للتطبيق بعرض الإشعارات + منح الإذن + الكافيين فعال + فعال + يتم إبقاء الشاشة مضاءة Ignore battery optimization Recommended for reliable service on some devices Abort with screen off Timeout Presets Select available durations for QS tile - 5m - 10m - 30m - 1h + 5 دقائق + 10 دقائق + 30 دقيقة + ساعة Starting in %1$ds… %1$s remaining @@ -367,9 +367,9 @@ Master toggle for app locking Select locked apps Choose which apps require authentication - Pick apps to freeze - Choose which apps can be frozen - Freeze all apps + اختر التطبيقات للتجميد + اختر التطبيقات التي يمكن تجميدها + تجميد جميع التطبيقات Immediately freeze all picked apps Freeze when locked Freeze selected apps when device locks @@ -378,8 +378,8 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. - Install Shizuku - Shizuku permission + تثبيت Shizuku + منح الإذن Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -401,8 +401,8 @@ Required to display the notification lighting overlay on the screen Device Administrator Required to hard-lock the device (disabling biometrics) on unauthorized access attempts - Grant Permission - Copy ADB + منح الإذن + نسخ الـADB Check Enable in Settings How to grant @@ -411,7 +411,7 @@ Essentials Freeze - Frozen + مُجمد DIY Disabled apps Do It Yourself @@ -420,19 +420,19 @@ Enable in settings Switch to Essentials Keyboard is active and ready! - Enabled + مفعل Disabled Adaptive Brightness Toggle adaptive brightness Maps Power Saving Toggle maps power saving mode Search for Tools, Mods and Tweaks - Search - Stop - Search + البحث + إيقاف + البحث Back - Done + تم Preview Help Guide Update Available @@ -454,8 +454,8 @@ Charger Disconnected Charging Screen On - Vibrate - Show Notification + إهتزاز + عرض الإشعارات Remove Notification Turn On Flashlight Turn Off Flashlight @@ -762,7 +762,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -781,13 +781,13 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + هل وصلنا بعد؟ + تنبيهات الوجهات القريبة Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. + افتح خرائط Google، واختر موقعًا، وشاركه مع Essentials. Radius: %d m Distance to trigger the alarm Enable notification @@ -813,7 +813,7 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active + منبه السفر مفعل %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination From 9830156d37c1f20569309ab467bd258eef24b234 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sun, 18 Jan 2026 14:33:39 +0530 Subject: [PATCH 057/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ad9d2ae4..6912804e 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -463,7 +463,7 @@ 暗い壁紙 このアクションでは、壁紙を暗くするために、ShizukuまたはRootが必要です。 トリガーを選択 - App + アプリ Automate based on open app ステータスを選択 アクションを選択 @@ -541,14 +541,14 @@ キーボード - height - padding - haptic - input + 高さ + 間隔 + 触覚 + 入力 - visible - alert + 表示 + アラート ライト @@ -629,31 +629,31 @@ 静かな - ignore - filter + 無視 + フィルター - automation - auto - lock + 自動化 + 自動 + ロック - adb - usb - debug + ADB + USB + デバッグ - battery - charge - power + バッテリー + 充電 + 電源 - blur - glass - vignette + ブラー + ガラス + ビネット - float + フロート window overlay From 1afb133fc5df1d311b87590549c4a0883cba1090 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sun, 18 Jan 2026 15:35:33 +0530 Subject: [PATCH 058/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 219380a5..5ef13b4f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -37,7 +37,7 @@ Options de la lampe-torche Ajuster la disparition et les autres paramètres Thème Noir Absolu - Utiliser un fond noir pure en mode sombre + Utiliser un fond noir pure en thème sombre Retour haptique Réattribuer l\'appui long Écran éteint @@ -48,15 +48,15 @@ Jouer/mettre en pause le média Prochain média Média précédent - Basculer le mode vibreur - Basculer le mode silencieux + Activer le mode vibreur + Activer le mode silencieux Assistant IA Prendre une capture d\'écran Quand l\'écran est éteint, appuyez longuement sur le bouton sélectionné pour déclencher son action assignée. Sur les appareils Pixel, cette action ne se déclenche que si le Always-On Display est activé à cause de limitations systèmes. Quand l\'écran est allumé, appuyez longuement sur le bouton sélectionné pour déclencher son action assignée. Intensité de la lampe-torche Fade in and out - Smoothly toggle flashlight + Allumer/éteindre de manière fluide la lampe-torche Contrôles globlaux Fade-in flashlight globally Ajuster l\'intensité @@ -175,7 +175,7 @@ Désactiver Sécurité d\'écran verrouillé - Protection de l\'écran de verrouillage + Écran de verrouillage Authentifiez-vous pour activer la protection de l\'écran de verrouillage Authentifiez-vous pour désactiver la protection de l\'écran de verrouillage ⚠️ ATTENTION @@ -238,7 +238,7 @@ Cacher les données mobiles quand un réseau Wi-Fi est connecté Cache les données mobiles dans certains modes Réinitialiser les icônes - Please note that the implementation of these options may depend on the OEM and some may not be functional at all. + Veuillez noter que l\'implémentation de ces options peut dépendre du constructeur et certaines peuvent ne pas fonctionner du tout. Autre @@ -262,7 +262,7 @@ Contrôler la visibilité des icônes de la barre d\'état Caffeinate Maintenir l\'écran allumé - Mode économie d\'énergie Maps + Économie d\'énergie Maps Pour tout appareil Android Éclairage de notifications Éclairer pour les notifications @@ -273,7 +273,7 @@ Afficher les appareils Bluetooth Afficher le niveau de batterie des appareils Bluetooth connectés Nombre d\'appareils max - Adjust max devices visible in widget + Ajuster le nombre d\'appareils maximium visibles sur le widget Fond du widget Afficher le fond du widget @@ -291,9 +291,9 @@ Voir tous Réattribution de boutons Réattribuer les boutons physiques de l\'appareil - Mode Éclairage nocturne dynamique - Bascule le mode Éclairage nocturne en fonction de l\'appli utilisée - Protection de l\'écran de verrouillage + Éclairage nocturne dynamique + Activer/désactiver le mode Éclairage nocturne en fonction de l\'appli utilisée + Écran de verrouillage Empêcher l\'utilisation des contrôles réseaux Verrouillage d\'applis Protéger les applications avec la biométrie @@ -460,8 +460,8 @@ Allumer la lampe-torche Éteindre la lampe-torche Activer/désactiver la lampe-torche - Dim Wallpaper - This action requires Shizuku or Root to adjust system wallpaper dimming. + Fond d\'écran assombri + Cette action nécessite Shizuku ou un accès racine (root) pour ajuster le fond d\'écran du système. Sélectionner un déclencheur Appli Automatisation basée sur l\'ouverture d\'appli @@ -480,10 +480,10 @@ Analyse des événements système pour vos automatisations Effets d\'appareil Désactiver les effets d\'appareil - Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. - Grayscale + Contrôler les effets systèmes comme l\'échelle des gris, la suppression de l\'Always-On Display, le changement de fond d\'écran et le mode nuit. + Échelle des gris Enlever l\'éclairage ambient - Dim Wallpaper + Fond d\'écran assombri Mode nuit Cette fonctionnalité nécessite Android 15 ou supérieur. Activé @@ -749,7 +749,7 @@ 2. Ouvrez le menu à 3 points en haut à droite et sélectionnez \"Autoriser les paramètres restreints\". Vous devrez peut-être vous authentifier avec la biométrie. Une fois fait, réessayez d\'accorder la permission. Shizuku Shziuku est un outil puissant qui permet aux applications d\'utiliser directement les APIs système directement avec ADB ou l\'accès racine (root). Il est requis pour des fonctionnalités comme le mode économie d\'énergie maps ou le gel d\'applis. Il aide également à donner certaines permissions comme WRITE_SECURE_SETTINGS. \n\nMais la version de Shizuku disponible sur le Play Store peut-être obsolète et sera probablement inutilisable sur les versions récentes d\'Android. Dans ce cas, merci d\'installer la dernière version depuis le GitHub officiel ou un fork mis à jour. - Mode économie d\'énergie Maps + Économie d\'énergie Maps This feature automatically triggers Google Maps power saving mode which is currently exclusive to the Pixel 10 series. A community member discovered that it is still usable on any Android device by launching the maps minMode activity with root privileges. \n\nAnd then, I had it automated with Tasker to automatically trigger when the screen turns off during a navigation session and then was able to achieve the same with just runtime Shizuku permissions. \n\nIt is intended to be shown over the AOD of Pixel 10 series so because of that, you may see an occasional message popping up on the display that it does not support landscape mode. That is not avoidable by the app and you can ignore. Mode silencieux You may have noticed that the silent mode also triggers DND. \n\nThis is due to how the Android implemented it as even if we use the same API to switch to vibrate mode, it for some reason turns on DND along with the silent mode and this is not avoidable at this moment. :( From 3a3e05f127f2fb65bd850cda57d3e79a675e8dbd Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sun, 18 Jan 2026 16:33:16 +0530 Subject: [PATCH 059/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 5ef13b4f..b4c1c037 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -343,28 +343,28 @@ Éclairage de notifications Toggle notification lighting service Éclairage nocturne dynamique - Night light automation toggle + Basculer l\'automatisation du mode Éclairage nocturne Sécurité de verrouillage Network security on lockscreen toggle Audio mono Basculer l\'audio mono forcé Lampe-torche - Dedicated flashlight toggle + Bouton lampe-torche dédié Gel d\'applis Lancer la grille de gel d\'applis Impulsion de lampe torche Toggle notification flashlight pulse - Toggle stay awake developer option + Basculer l\'option développeur \"Rester allumé\" Activer la réattribution de boutons - Master toggle for volume button remapping + Basculer globalement la réattribution du bouton volume Reconfigurer le retour haptique Vibration quand le bouton réattribué est appuyé Basculer la lampe-torche Basculer la lampe-torche avec les boutons volume Éclairage nocturne dynamique - Master switch for dynamic night light + Basculer globalement l\'éclairage nocturne dynamique Activer le verrouillage d\'applis - Master toggle for app locking + Basculer globalement le verrouillage d\'applis Sélectionner les applis verrouillées Choisir quelles applis nécessitent l\'authentification Choisir les applis à geler @@ -398,7 +398,7 @@ Modifier les paramètres système Requise pour activer/désactiver la luminosité adaptative et d\'autres paramètres système Permission de superposition - Required to display the notification lighting overlay on the screen + Requise pour afficher la superposition de l\'éclairage de notification sur l\'écran Administrateur de l\'appareil Requise pour faire un verrouillage forcé de l\'appareil (désactiver la biométrie) lors d\'accès non autorisés Accorder la permission From 9937808ee5da20d84f8bdf9a4631c199ba2070fb Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sun, 18 Jan 2026 19:25:48 +0530 Subject: [PATCH 060/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index b4c1c037..2678f8ed 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -181,7 +181,7 @@ ⚠️ ATTENTION Cette fonctionnalité n\'est pas parfaite. Dans certaines situations il est possible que quelqu\'un puisse tout de même interagir avec les blocs de paramètres rapides. \nGardez également à l\'esprit qu\'Android va toujours autoriser les redémarrages forcés et les Pixels vont toujours permettre d\'éteindre l\'appareil depuis l\'écran de verrouillage. Veuillez retirer le bloc des \"Réglages rapides\" du Mode Avion car il ne peut être bloqués (il n\'ouvre aucune fenêtre que l\'appli peut bloquer). - When enabled, the Quick Settings panel will be immediately closed and the device will be locked down if someone attempt to interact with Internet tiles while the device is locked. \n\nThis will also disable biometric unlock to prevent further unauthorized access. Animation scale will be reduced to 0.1x while locked to make it even harder to interact with. + Une fois activé, le menu \"Réglages rapides\" sera immédiatement fermé et l\'appareil sera verrouillé si quelqu\'un essaye d\'interagir avec les blocs de réglages réseaux quand l\'appareil est verrouillé. \n\nCela va également désactiver l\'authentification biométrique pour prévenir tout accès non autorisé. L\'échelle des animations va également être réduite à x0,1 quand l\'appareil est verrouillé pour rendre encore plus difficile l\'intéraction avec le menu. Réorganiser les modes Appuyez longuement pour basculer @@ -280,7 +280,7 @@ Déclencher une automatisation Prévoir une action à déclencher lors d\'une observation Automatisation de l\'état - Schedule an action to execute based on the state of a condition in and out + Prévoir une action à exécuter basé sur l\'état d\'une condition Nouvelle automatisation Modifier l\'automatisation Actions de lien @@ -301,7 +301,7 @@ Désactive les applications rarement utilisées Retour haptique du Widget - Pick haptic feedback for widget taps + Choisir un retour haptique pour les appuis de widget Wi-Fi intelligent Cacher les données mobiles quand un réseau Wi-Fi est connecté Données intelligentes @@ -309,7 +309,7 @@ Réinitialiser les icônes Réinitialiser la visibilité des icônes de la barre d\'état Arrêter Caffeinate au verrouillage - Automatically turn off Caffeinate when manually locking the device + Automatiquement désactiver Caffeinate quand vous verrouillez manuellement l\'appareil Style d\'éclairage Choose between Stroke, Glow, Spinner, and more Rayon des coins @@ -341,11 +341,11 @@ Mode de sonnerie Changer le mode de sonnerie (Sonnerie/Vibreur/Silencieux) Éclairage de notifications - Toggle notification lighting service + Activer/désactiver le service de notifications lumineuses Éclairage nocturne dynamique Basculer l\'automatisation du mode Éclairage nocturne Sécurité de verrouillage - Network security on lockscreen toggle + Activer/désactiver la protection de l\'écran de verrouillage Audio mono Basculer l\'audio mono forcé Lampe-torche @@ -353,7 +353,7 @@ Gel d\'applis Lancer la grille de gel d\'applis Impulsion de lampe torche - Toggle notification flashlight pulse + Activer/désactiver l\'impulsion de lampe-torche Basculer l\'option développeur \"Rester allumé\" Activer la réattribution de boutons Basculer globalement la réattribution du bouton volume From 6b76d9abf181351597ce128f1cb0619d9db352c3 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Sun, 18 Jan 2026 20:38:06 +0530 Subject: [PATCH 061/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 100 ++++++++++++------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2678f8ed..7a15c4eb 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -12,8 +12,8 @@ Widget de verrouillage invisible Gel d\'applis Impulsion de lampe torche - Vérifier pour des pré-versions - Peut être instable + Obtenir les préversions (bêta) + Peut-être instable Sécurité Activer le verrouillage d\'applis @@ -27,7 +27,7 @@ Mais également, la fenêtre d\'authentification biométrique ne vous laisse utiliser que des méthodes d\'authentification FORTES. Le déverrouillage par reconnaissance faciale est considéré comme FAIBLE pour les appareils comme les Pixel 7 qui ne pourront utiliser que les autres méthodes FORTES disponibles comme l\'authentification par empreinte digitale ou code PIN. Activer la réattribution de boutons - Use Shizuku + Utiliser Shizuku ou l\'accès racine (root) Fonctionne avec l\'écran éteint (Recommandé) Shizuku n\'est pas actif %1$s Détecté @@ -55,10 +55,10 @@ Quand l\'écran est éteint, appuyez longuement sur le bouton sélectionné pour déclencher son action assignée. Sur les appareils Pixel, cette action ne se déclenche que si le Always-On Display est activé à cause de limitations systèmes. Quand l\'écran est allumé, appuyez longuement sur le bouton sélectionné pour déclencher son action assignée. Intensité de la lampe-torche - Fade in and out + Fondu d\'entrée et de sortie Allumer/éteindre de manière fluide la lampe-torche Contrôles globlaux - Fade-in flashlight globally + Fondu global de lampe-torche Ajuster l\'intensité Les boutons Volume + et - ajustent l\'intensité de lampe-torche Mise à jour en direct @@ -87,7 +87,7 @@ 1h Démarrage dans %1$ds… - %1$s RESTANT + %1$s restant Notification persistante pour Caffeinate Activer le mode d\'Éclairage nocturne dynamique @@ -125,9 +125,9 @@ Impulsion de lampe torche Seulement quand le téléphone est vers le bas Style - Stroke adjustment + Ajustement du trait Rayon des coins - Stroke thickness + Épaisseur du trait Ajustement de la brillance Propagation de la brillance Placement @@ -248,7 +248,7 @@ Outils Visuels Système - Sécurité & Confidentialité + Sécurité et confidentialité Recherchez des outils, des mods, ou des améliorations Aucun résultat pour \"%1$s\" @@ -273,7 +273,7 @@ Afficher les appareils Bluetooth Afficher le niveau de batterie des appareils Bluetooth connectés Nombre d\'appareils max - Ajuster le nombre d\'appareils maximium visibles sur le widget + Ajuster le nombre d\'appareils maximum visibles sur le widget Fond du widget Afficher le fond du widget @@ -296,7 +296,7 @@ Écran de verrouillage Empêcher l\'utilisation des contrôles réseaux Verrouillage d\'applis - Protéger les applications avec la biométrie + Protéger les applis avec la biométrie Gel d\'applis Désactive les applications rarement utilisées @@ -311,9 +311,9 @@ Arrêter Caffeinate au verrouillage Automatiquement désactiver Caffeinate quand vous verrouillez manuellement l\'appareil Style d\'éclairage - Choose between Stroke, Glow, Spinner, and more + Choisissez entre trait, brillance, rotation et plus encore Rayon des coins - Adjust the corner radius of the notification lighting + Ajuster le rayon des coins des notifications lumineuses Ignorer les notifications silencieuses Ne pas afficher de lumière pour les notifications silencieuses Impulsion de lampe torche @@ -321,7 +321,7 @@ Seulement quand le téléphone est vers le bas Impulsion de lampe torche seulement quand l\'appareil est vers le bas Désactiver les notifications de débogage - Cacher les notifications de débogage ADB/USB persistantes. + Cacher les notifications de débogage ADB/USB persistantes Désactiver la notification de transfert de fichiers Cache les notifications de transfert de fichiers USB persistantes Désactiver la notification de recharge @@ -359,7 +359,7 @@ Basculer globalement la réattribution du bouton volume Reconfigurer le retour haptique Vibration quand le bouton réattribué est appuyé - Basculer la lampe-torche + Allumer/éteindre la lampe-torche Basculer la lampe-torche avec les boutons volume Éclairage nocturne dynamique Basculer globalement l\'éclairage nocturne dynamique @@ -379,19 +379,19 @@ Shizuku Requis pour les commandes avancées. Installez Shizuku depuis le Play Store. Installer Shizuku - Shizuku permission + Accorder la permission Requise pour exécuter des commandes d\'économie d\'énergie pendant la navigation de Maps. Accès racine (root) - Permissions requises pour les actions systèmes en utilisant les privilèges racine (root) + Permissions requises pour les actions systèmes en utilisant les privilèges racine (root). Analyseur de notification - Requise pour détecter quand Maps est en navigation + Requise pour détecter quand Maps est en navigation. Requise pour détecter les nouvelles notifications Requise pour détecter et mettre en attente les notifications Service d\'accessibilité - Requis pour le verrouillage d\'applis, le widget de verrouillage et d\'autres fonctionnalités pour détecter les interactions. + Requis pour le verrouillage d\'applis, le widget de verrouillage et d\'autres fonctionnalités pour détecter les interactions Requis pour déclencher l\'éclairage lors de la réception d\'une nouvelle notification Requis pour intercepter les appuis sur les boutons physiques - Requis pour surveiller les applications en avant-plan + Requis pour surveiller les applications en avant-plan. Modifier les paramètres de sécurité Requise pour les icônes de la barre d\'état et la protection de l\'écran de verrouillage Requise pour activer/désactiver l\'Éclairage nocturne. Autoriser via ADB ou root. @@ -444,31 +444,31 @@ Déclencheur État Action - In - Out + Entrée + Sortie Automatisation Écran éteint Écran allumé Déverrouillage de l\'appareil - Chargeur connecté - Chargeur déconnecté - + Câble branché + Câble débranché + En charge Écran allumé Vibreur Afficher une notification Supprimer la notification Allumer la lampe-torche Éteindre la lampe-torche - Activer/désactiver la lampe-torche + Allumer/éteindre la lampe-torche Fond d\'écran assombri - Cette action nécessite Shizuku ou un accès racine (root) pour ajuster le fond d\'écran du système. - Sélectionner un déclencheur + Cette action nécessite Shizuku ou un accès racine (root) pour ajuster l\'assombrissement du fond d\'écran. + Déclencheur Appli Automatisation basée sur l\'ouverture d\'appli - État de sélection - Action de sélection - In Action - Out Action + État + Action + Entrée + Sortie Annuler Enregistrer Modifier @@ -480,7 +480,7 @@ Analyse des événements système pour vos automatisations Effets d\'appareil Désactiver les effets d\'appareil - Contrôler les effets systèmes comme l\'échelle des gris, la suppression de l\'Always-On Display, le changement de fond d\'écran et le mode nuit. + Contrôler les effets systèmes comme l\'échelle des gris, la suppression de l\'Always-On Display, l\'assombrissement du fond d\'écran et le mode nuit. Échelle des gris Enlever l\'éclairage ambient Fond d\'écran assombri @@ -516,7 +516,7 @@ Authentifiez-vous pour accéder aux paramètres Authentifiez-vous pour activer cette fonctionnalité Authentifiez-vous pour désactiver cette fonctionnalité - %1$s Paramètres + Paramètres de \"%1$s\" fonctionnalité paramètres cacher @@ -714,7 +714,7 @@ Éteindre Luminosité de la lampe-torche - Paramètres de %1$s + Paramètres de \"%1$s\" Confirmez votre identité pour ouvrir les paramètres Authentification requise Confirmez votre identité @@ -734,7 +734,7 @@ Avatar du développeur Aide & Guides - Besoin de plus d\'aide ? Contactez-nous, + Besoin de plus d\'aide ? Contactez-nous : Réduire Étendre Groupe de support @@ -745,28 +745,28 @@ Permissions d\'accessibilité, de notification et de superposition Vous pouvez obtenir ce message d\'accès refusé si vous essayer d\'accorder des permissions sensibles telles que l\'accessibilité, l\'analyse des notifications ou les permissions de superposition. Pour l\'accorder, regardez les étapes ci-dessous. - 1. Allez dans la page d\'informations de l\'application Essentials + 1. Allez dans la page d\'informations de l\'application Essentials. 2. Ouvrez le menu à 3 points en haut à droite et sélectionnez \"Autoriser les paramètres restreints\". Vous devrez peut-être vous authentifier avec la biométrie. Une fois fait, réessayez d\'accorder la permission. Shizuku Shziuku est un outil puissant qui permet aux applications d\'utiliser directement les APIs système directement avec ADB ou l\'accès racine (root). Il est requis pour des fonctionnalités comme le mode économie d\'énergie maps ou le gel d\'applis. Il aide également à donner certaines permissions comme WRITE_SECURE_SETTINGS. \n\nMais la version de Shizuku disponible sur le Play Store peut-être obsolète et sera probablement inutilisable sur les versions récentes d\'Android. Dans ce cas, merci d\'installer la dernière version depuis le GitHub officiel ou un fork mis à jour. Économie d\'énergie Maps - This feature automatically triggers Google Maps power saving mode which is currently exclusive to the Pixel 10 series. A community member discovered that it is still usable on any Android device by launching the maps minMode activity with root privileges. \n\nAnd then, I had it automated with Tasker to automatically trigger when the screen turns off during a navigation session and then was able to achieve the same with just runtime Shizuku permissions. \n\nIt is intended to be shown over the AOD of Pixel 10 series so because of that, you may see an occasional message popping up on the display that it does not support landscape mode. That is not avoidable by the app and you can ignore. + Cette fonctionnalité déclenche automatiquement le mode économie d\'énergie de Google Maps qui est actuellement réservé à la série des Pixel 10. Un membre de la communauté à découvert qu\'il est tout de même utilisable sur n\'importe quel appareil Android en lançant l\'activité minMode avec les privilèges racine (root). \n\nDonc, je l\'ai automatisé avec Tasker pour le déclencher automatiquement quand l\'écran est éteint pendant une session de navigation et j\'ai pu faire la même chose en n\'utilisant que les permissions Shizuku. \n\nCette fonctionnalité est prévue pour s\'afficher par dessus le Always-On Display de la série Pixel 10 donc vous devriez occasionnellement voir apparaître sur l\'écran un message concernant l\'incompatibilité avec le mode paysage. Cette application ne peut empêcher ça mais il suffit de les ignorer. Mode silencieux - You may have noticed that the silent mode also triggers DND. \n\nThis is due to how the Android implemented it as even if we use the same API to switch to vibrate mode, it for some reason turns on DND along with the silent mode and this is not avoidable at this moment. :( + Vous avez peut-être remarqué que le mode silencieux déclenche également le mode \"Ne pas déranger\". \n\nCeci est dû à comment Android l\'a l\'implémenté et même si on utilise la même API pour passer en mode vibreur, pour quelconque raison cela active le mode NPD en parallèle du mode silencieux et ce n\'est possible de faire autrement pour l\'instant :( Qu\'est-ce que le gel ? Faites une pause et séparez-vous des applis qui vous distraient tout en économisant un peu d\'énergie en empêchant les applis de tourner en arrière-plan. Convient pour les applis rarement utilisées. \n\nNon recommandé pour les services de communication car ils ne vous enverront aucune notification à moins de les dégeler. \n\nIl est fortement recommandé de ne pas geler des applis systèmes car cela peut causer une instabilité du système. Procédez avec prudence, vous avez été avertis. \n\nInspiré par Hail <3 Est-ce que le verrouillage d\'applis et la protection de l\'écran de verrouillage sont réellement sécurisés ? - Absolument pas. \n\nToute appli tierce ne peut interférer à 100% avec les interactions normales d\'un appareil et le verrouillage d\'applis n\'est qu\'une superposition pour empêcher d\'interagir avec elles. Il y a toujours des moyens de le contourner et il n\'est pas parfait. \n\nDe même pour la fonctionnalité de protection de l\'écran de verrouillage qui détecte simplement une tentative d\'action avec les blocs de réglages réseau dans les réglages rapides et se superpose pour empêcher leur utilisation, ils sont toujours disponibles pour quiconque sur les appareils Pixels et si quelqu\'un souhaite réellement désactiver le réseau il le pourra, surtout avec un bloc de réglage du Mode Avion que cette appli ne peut bloquer. \n\nCes fonctionnalités sont expérimentales et ne devraient pas être considérées comme des sécurités fortes. \n\nAlternatives sécurisées :\n - Verrouillage d\'applis : Le \"Dossier Sécurisé\" et \"Espace privé\" sur les Pixels et Samsung.\n - Empêcher l\'accès aux réseaux mobiles : Vérifiez que les paramètres de protection contre le vol ainsi que ceux de localisation hors-ligne/éteint soient activés sur l\'appareil. Vous devriez également envisager d\'installer Graphene OS pour une meilleure sécurité. + Absolument pas. \n\nAucune appli tierce ne peut interférer à 100% avec le comportement normale d\'un appareil et le verrouillage d\'applis n\'est qu\'une superposition pour empêcher d\'interagir avec les applications. Il y a toujours des moyens de le contourner et ce n\'est pas un système parfait. \n\nDe même pour la fonctionnalité de protection de l\'écran de verrouillage qui détecte simplement une tentative d\'action avec les blocs de réglages réseau dans les réglages rapides et se superpose pour empêcher leur utilisation, ils sont toujours disponibles pour quiconque sur les appareils Pixels et si quelqu\'un souhaite réellement désactiver le réseau il le pourra, surtout avec un bloc de réglage du Mode Avion que cette appli ne peut bloquer. \n\nCes fonctionnalités sont expérimentales et ne devraient pas être considérées comme sûres. \n\nAlternatives sécurisées :\n - Verrouillage d\'applis : Le \"Dossier Sécurisé\" et \"Espace privé\" sur les Pixels et Samsung.\n - Empêcher l\'accès aux réseaux mobiles : Vérifiez que les paramètres de protection contre le vol ainsi que ceux de localisation hors-ligne/éteint soient activés sur l\'appareil. Vous devriez également envisager d\'installer Graphene OS pour une meilleure sécurité. Icônes de la barre d\'état - You may notice that even after resetting the statusbar icons, Some icons such as device rotation, wired headphone icons may stay visible. This is due to how the statubar blacklist is implemented in Android and how your OEM may have customized them. \nYou may need further adjustments. \n\nAlso not all icon visibility options may work as they depend on the OEM implementations and availability. + Vous avez peut-être remarqué que même après avoir réinitialisé les icônes de la barre d\'état, certaines icônes comme celles de la rotation de l\'appareil ou les écouteurs filaires peuvent rester visibles. Ceci est dû à comment la liste noire de la barre d\'état est implémenté dans Android et de comment votre constructeur les a personnalisé. \nVous pourriez avoir à effectuer plus d\'ajustements. \n\nDe plus, toutes les options de visibilité des icônes peuvent ne pas marcher car elle dépendent de l\'implémentation faite par votre constructeur et la disponibilité. L\'éclairage de notification ne marche pas Cela dépend du constructeur. Certains comme OneUI n\'ont pas l\'air d\'autoriser les superpositions au-dessus de l\'Always-On Display ce qui empêche les effets de lumière d\'être affichés. Dans ce cas, essayez l\'éclairage ambiant comme solution. La réatttribution de boutons ne marche pas quand l\'écran est éteint Certains constructeurs limitent le service d\'accessibilité quand l\'écran est éteint mais peuvent toujours marcher si le Always-On Display est actif. \nDans ce cas, vous pouvez peut-être utiliser les boutons réattribués avec l\'Always-On Display allumé, mais pas lorsqu\'il est éteint. \n\nComme solution, vous devrez utiliser la permission Shizuku et activer \"Utiliser Shizuku ou l\'accès racine (root)\" dans les paramètres de réattributions de boutons de l\'application ce qui permet d\'identifier des saisies physiques.\nCe n\'est pas garanti que cela fonctionne sur tous les appareils et a besoin d\'être testé.\n\nEt même si elle st activée, la méthode Shizuku ne sera utilisée qu\'en cas de besoin. Autrement, le service d\'accessibilité sera utilisé. Le contrôle de la luminosité de la lampe-torche ne marche pas - Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu + Seul un nombre limité d\'appareil a le support matériel et logiciel pour ajuster la luminosité de lampe-torche. \n\nLa version minimum est Android 13 (SDK 33). \nLe contrôle de la luminosité de la lampe-torche supporte uniquement HAL 3.8 ou ultérieur, donc parmis les appareils supportés, les plus récents (Par exemple, Pixel 6/7, Galaxy S23, etc...)\'\npolodarb/Flashlight-Tiramisu À quoi sert cette application ? - Good question,\n\nI always wanted to extract the most out of my devices as I\'ve been a rooted user for ever since I got my first Project Treble device. And I\'ve been loving the Tasker app which is like the god when comes automation and utilizing every possible API and internal features of Android.\n\nSo I am not unrooted and back on stock Android beta experience and wanted to get the most out from what is possible with given privileges. Might as well share them. So with my beginner knowledge in Kotlin Jetpack and with the support of many research and assist tools and also the great community, I built an all-in-one app containing everything I wanted to be in my Android with given permissions. And here it is.\n\nFeature requests are welcome, I will consider and see if they are achievable with available permissions and my skills. Nowadays what is not possible. :)\n\nWhy not on Play Store?\nI don\'t wanna risk getting my Developer account banned due to the highly sensitive and internal permissions and APIs being used in the app. But with the way Android sideloading is headed, let\'s see what we have to do. I do understand the concerns of sideloaded apps being malicious.\nWhile we are at the topic, Checkout my other app AirSync if you are a mac + Android user. *shameless plug*\n\nEnjoy, Keep building! (っ◕‿◕)っ + Bonne question \n\nJ\'ai toujours voulu obtenir le plus possible de mes appareils et j\'ai toujours été un utilisateur root depuis que j\'ai obtenu mon premier appareil Project Treble. J\'ai adoré l\'application Takser qui est divine pour les automatisations et utiliser toutes les APIs et fonctionnalités internes disponibles dans Android. \n\nDonc j\'ai réinstallé Android d\'origine en bêta pour essayer de faire le plus de choses possibles avec les permissions qu\'on nous donne. Et même les partager. Donc avec mes connaissances de débutant en Kotlin Jetpack et avec de nombreuses recherches et d\'outils d\'assistance ainsi que la superbe communauté, j\'ai construit une application tout-en-un qui contiendrait tout ce que je veux dans mon Android avec les permissions disponibles. Et voilà.\n\nLes demandes de nouvelles fonctionnalités sont disponibles, je les étudierai et je les implémenterai dans la mesure du possible et de mes compétences. De nos jours, qu\'est-ce qui est impossible ? :)\n\nPourquoi ne pas être sur le Play Store ?\nJe ne veux pas risquer le bannissement de mon compte développeur car l\'application demande l\'accès à des permissions très sensibles . Je comprends que certains peuvent se méfier d\'application externes qui pourraient être malicieuses.\nD\'ailleurs en parlant de ça, jetez un coup d\'oeil à mon autre appli AirSync si vous êtes un utilisateur Mac + Android.\n\nProfitez, bon bricolage! (っ◕‿◕)っ Signaler un bogue Signalement copié dans le presse-papiers @@ -799,7 +799,7 @@ Vous êtes arrivé à destination. Actuellement à %1$.1fkm Supprimer la destination - Traitement de la position... + Traitement de la position… DISTANCE RESTANTE Calcul… Arrêter le traçage @@ -810,12 +810,12 @@ Aucune destination Ouvrir Maps Permission d\'alarme en plein écran - Requise pour réveiller votre appareil à l\'arrivée. Appuyez pour autoriser + Requise pour réveiller votre appareil à l\'arrivée. Appuyez pour autoriser. %1$d m %1$.1f km Alarme de trajet active - %1$s restant (%2$d%%) - Progrès du voyage : + %1$s restant + Progrès du trajet Affiche la distance de la destination en temps réel Destination à proximité Préparer à partir @@ -830,20 +830,20 @@ Personnaliser l\'apparence et le comportement Hauteur du clavier Ajuster la taille verticale totale du clavier - Bottom Padding + Écart du bas Ajouter de l\'espace en dessous du clavier Retour haptique Vibrer à l\'appui sur une touche Tester le clavier Tester le clavier Hauteur du clavier - Bottom Padding + Écart du bas Retour haptique Arrondi des touches Arrondi des touches Ajuster le rayon des coins des touches Déplacer les fonctions en bas - Functions side padding + Écart latéral des fonctions Force du retour haptique Forme du clavier Rond From 296d8e01ed32ad0798df1a96b091f646889f57b5 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Mon, 19 Jan 2026 19:15:04 +0530 Subject: [PATCH 062/254] New translations strings.xml (Romanian) --- app/src/main/res/values-ro/strings.xml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 6d8c5d91..38b81a1b 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -10,12 +10,12 @@ Deschide Înghețarea aplicațiilor Aplicații înghețate Empty screen off widget - App Freezing + Înghețarea aplicațiilor Flashlight Pulse Check for pre-releases Might be unstable - Security + Securitate Enable app lock App Lock Security Authenticate to enable app lock @@ -29,11 +29,11 @@ Enable Button Remap Use Shizuku Works with screen off (Recommended) - Shizuku is not running - Detected %1$s - Status: %1$s - Open Shizuku - Flashlight + Shizuku nu rulează + Detectat %1$s + Stare: %1$s + Deschide Shizuku + Lanternă Flashlight options Adjust fading and other settings Pitch black theme @@ -66,9 +66,9 @@ Other Always turn off flashlight Even while display is on - Settings + Setări - Notification + Notificări Show Notification Post Notifications Allows the app to show notifications @@ -124,7 +124,7 @@ Flashlight Pulse Flashlight pulse Only while facing down - Style + Stil Stroke adjustment Corner radius Stroke thickness @@ -171,8 +171,8 @@ Active Inactive NFC - On - Off + Pornit + Oprit Screen locked security Screen Locked Security From c60e63ea05fa402e07038d987b83e9a8d29c8e83 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Mon, 19 Jan 2026 19:15:05 +0530 Subject: [PATCH 063/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 6912804e..56014c6d 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -654,32 +654,32 @@ フロート - window - overlay + ウインドウ + オーバーレイ - always - display - clock + 常に + 画面 + 時計 - audio - mute - volume + オーディオ + ミュート + 音量 - blue - filter - auto + + フィルター + 自動 - freeze - shizuku + フリーズ + Shizuku - manual - now - shizuku + マニュアル + + Shizuku proximity @@ -820,7 +820,7 @@ Destination Nearby Prepare to get off Dismiss - Destination set: %1$.4f, %2$.4f + 目的地をセット:%1$.4f, %2$.4f Rootを使う Shizukuの代わりに Rootアクセスができません。Rootマネージャーを確認してください。 From 074a4a581b44bbd92cd5c3fa9bd88cb84b01f4d6 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Wed, 21 Jan 2026 12:44:05 +0530 Subject: [PATCH 064/254] New translations strings.xml (German) --- app/src/main/res/values-de/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3ee08fca..d0686be6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -74,7 +74,7 @@ Erlaubt es der App Benachrichtigungen zu zeigen Berechtigung gewähren Caffeinate Active - Active + Aktiv Screen is being kept awake Ignore battery optimization Recommended for reliable service on some devices @@ -167,7 +167,7 @@ Taschenlampenimpuls Wach bleiben Essentials Keyboard - English (US) + Englisch (US) Aktiv Inaktiv NFC @@ -270,7 +270,7 @@ Kachel in den Schnelleinstellungen zum Umschalten des Tonmodus \'Wach bleiben\'-Kachel Kachel in den Schnelleinstellungen zum Umschalten des Wachbleibens - Show Bluetooth devices + Bluetooth-Geräte anzeigen Display battery level of connected Bluetooth devices Limit max devices Adjust max devices visible in widget From 2c32786554ba676d796400d3cfd46729c17a5bfa Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Wed, 21 Jan 2026 14:21:57 +0530 Subject: [PATCH 065/254] New translations strings.xml (German) --- app/src/main/res/values-de/strings.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d0686be6..c0243ba2 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -271,7 +271,7 @@ \'Wach bleiben\'-Kachel Kachel in den Schnelleinstellungen zum Umschalten des Wachbleibens Bluetooth-Geräte anzeigen - Display battery level of connected Bluetooth devices + Zeige den Akkustand von verbundenen Bluetooth-Geräten Limit max devices Adjust max devices visible in widget Widget background @@ -395,7 +395,7 @@ Geschützte Einstellungen umschreiben Benötigt für Statusleisten-Symbole und Sicherheit bei Bildschirmsperre Benötigt, um das Nachtlicht zu aktivieren. Über ADB oder Root gewähren. - Modify System Settings + Systemeinstellungen ändern Required to toggle Adaptive Brightness and other system settings Overlay-Berechtigung Benötigt, um das Benachrichtigungslicht auf dem Bildschirm anzuzeigen @@ -406,8 +406,8 @@ Prüfen In den Einstellungen aktivieren Wie man gewährt - Battery Optimization - Ensure the service is not killed by the system to save power. + Akkuoptimierung + Stelle sicher, dass der Service nicht von dem System gestoppt wird, um Strom zu sparen. Essentials Einfrieren @@ -420,12 +420,12 @@ Enable in settings Switch to Essentials Keyboard is active and ready! - Enabled - Disabled - Adaptive Brightness - Toggle adaptive brightness - Maps Power Saving - Toggle maps power saving mode + Aktiviert + Deaktiviert + Adaptive Helligkeit + Adaptive Helligkeit umschalten + Maps-Energiesparmodus + Maps-Energiesparmodus umschalten Suche nach Werkzeugen, Modifikationen und Anpassungen Suche Stop @@ -486,8 +486,8 @@ Dim Wallpaper Night Mode This feature requires Android 15 or higher. - Enabled - Disabled + Aktiviert + Deaktiviert Sameera Wijerathna Die alles umfassende Werkzeugkiste für Ihr Pixel und Android-Geräte @@ -849,7 +849,7 @@ Round Flat Inverse - Batteries + Akkus Monitor your device battery levels Battery Status Connect to AirSync From 73fa80be2f4c04c8fd059560b2a1d2f25c081cd8 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Thu, 22 Jan 2026 20:31:30 +0530 Subject: [PATCH 066/254] Fix #128 Realme service crash issue --- .../services/NotificationLightingService.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt b/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt index d98d9392..afb6d484 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt @@ -54,6 +54,14 @@ class NotificationLightingService : Service() { super.onCreate() createNotificationChannel() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + try { + startForeground(NOTIF_ID, buildNotification()) + } catch (_: Exception) { + // ignore foreground start failures on certain OEMs + } + } + // Register screen on/off receiver to attempt to re-show overlay when screen state changes screenReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -135,15 +143,6 @@ class NotificationLightingService : Service() { return START_NOT_STICKY } - // Ensure the process calls startForeground quickly when started via startForegroundService - // to avoid RemoteServiceException (ForegroundServiceDidNotStartInTimeException). - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - try { - startForeground(NOTIF_ID, buildNotification()) - } catch (_: Exception) { - // ignore foreground start failures; we'll continue - } - } // If accessibility service is enabled, delegate showing to it for higher elevation if (isAccessibilityServiceEnabled()) { From 66146d4b978f6ccb4661bf78feab45cd4939771f Mon Sep 17 00:00:00 2001 From: sameerasw Date: Thu, 22 Jan 2026 20:44:50 +0530 Subject: [PATCH 067/254] Fix #136 lcoation tracking crash --- .../sameerasw/essentials/services/LocationReachedService.kt | 4 ++-- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/services/LocationReachedService.kt b/app/src/main/java/com/sameerasw/essentials/services/LocationReachedService.kt index a514a93b..619b2820 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/LocationReachedService.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/LocationReachedService.kt @@ -78,7 +78,7 @@ class LocationReachedService : Service() { } } - private fun stopTracking() { + private fun stopTracking() { val alarm = repository.getAlarm() repository.saveAlarm(alarm.copy(isEnabled = false)) stopSelf() @@ -170,7 +170,7 @@ class LocationReachedService : Service() { else getString(R.string.location_reached_dist_km, it) } ?: getString(R.string.location_reached_calculating) - val contentText = getString(R.string.location_reached_service_remaining, distanceText) + val contentText = getString(R.string.location_reached_service_remaining, distanceText, progress) if (Build.VERSION.SDK_INT >= 35) { val builder = Notification.Builder(this, CHANNEL_ID) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 7a15c4eb..660e0de1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -814,7 +814,7 @@ %1$d m %1$.1f km Alarme de trajet active - %1$s restant + %1$s restant (%2$d%%) Progrès du trajet Affiche la distance de la destination en temps réel Destination à proximité diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 25cc7fe2..0dc81050 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -876,7 +876,7 @@ %1$d m %1$.1f km Travel alarm active - %1$s remaining + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From 12237b1875644f50e647baf1e989d1ca1883e26c Mon Sep 17 00:00:00 2001 From: sameerasw Date: Thu, 22 Jan 2026 21:04:31 +0530 Subject: [PATCH 068/254] Fix #107 Only use shizuku while display is off --- .../tiles/ScreenOffAccessibilityService.kt | 30 +++++++++++++ .../configs/ButtonRemapSettingsUI.kt | 44 +++---------------- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt b/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt index 31a61347..b22d8235 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt @@ -68,10 +68,12 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene Intent.ACTION_SCREEN_ON -> { notificationLightingHandler.onScreenOn() freezeHandler.removeCallbacks(freezeRunnable) + stopInputEventListener() } Intent.ACTION_SCREEN_OFF -> { appFlowHandler.clearAuthenticated() scheduleFreeze() + startInputEventListenerIfEnabled() } Intent.ACTION_USER_PRESENT -> { securityHandler.restoreAnimationScale() @@ -131,6 +133,7 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene sensorManager.unregisterListener(this) securityHandler.restoreAnimationScale() notificationLightingHandler.removeOverlay() + stopInputEventListener() serviceScope.cancel() super.onDestroy() } @@ -198,4 +201,31 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene } return super.onStartCommand(intent, flags, startId) } + + private fun startInputEventListenerIfEnabled() { + val prefs = getSharedPreferences("essentials_prefs", MODE_PRIVATE) + val isEnabled = prefs.getBoolean("button_remap_enabled", false) + val useShizuku = prefs.getBoolean("button_remap_use_shizuku", false) + + if (isEnabled && useShizuku) { + try { + val intent = Intent(this, InputEventListenerService::class.java) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + startForegroundService(intent) + } else { + startService(intent) + } + } catch (e: Exception) { + // Ignore + } + } + } + + private fun stopInputEventListener() { + try { + stopService(Intent(this, InputEventListenerService::class.java)) + } catch (e: Exception) { + // Ignore + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt index ec5a104e..269cef05 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt @@ -128,59 +128,25 @@ fun ButtonRemapSettingsUI( if (shellHasPermission) { viewModel.setButtonRemapUseShizuku(true, context) - ContextCompat.startForegroundService( - context, - Intent(context, InputEventListenerService::class.java) - ) } else if (shellIsAvailable && !isRootEnabled) { - // Shizuku logic (still needed for specific permission request if not granted) + // Shizuku logic shizukuHelper.requestPermission { _, grantResult -> if (grantResult == android.content.pm.PackageManager.PERMISSION_GRANTED) { viewModel.setButtonRemapUseShizuku(true, context) - ContextCompat.startForegroundService( - context, - Intent( - context, - InputEventListenerService::class.java - ) - ) } } } else if (isRootEnabled && !shellHasPermission) { - // Root logic - try to run a command to trigger su prompt + // Root logic viewModel.setButtonRemapUseShizuku(true, context) - com.sameerasw.essentials.utils.ShellUtils.runCommand( - context, - "id" - ) - if (com.sameerasw.essentials.utils.ShellUtils.hasPermission( - context - ) - ) { - ContextCompat.startForegroundService( - context, - Intent(context, InputEventListenerService::class.java) - ) - } + com.sameerasw.essentials.utils.ShellUtils.runCommand(context, "id") } else { // Provider not running viewModel.setButtonRemapUseShizuku(true, context) - val toastRes = - if (isRootEnabled) R.string.root_not_available_toast else R.string.shizuku_not_running_toast - android.widget.Toast.makeText( - context, - context.getString(toastRes), - android.widget.Toast.LENGTH_SHORT - ).show() + val toastRes = if (isRootEnabled) R.string.root_not_available_toast else R.string.shizuku_not_running_toast + android.widget.Toast.makeText(context, context.getString(toastRes), android.widget.Toast.LENGTH_SHORT).show() } } else { viewModel.setButtonRemapUseShizuku(false, context) - context.stopService( - Intent( - context, - InputEventListenerService::class.java - ) - ) } }, modifier = Modifier.highlight(highlightSetting == "shizuku_remap") From 00d0633a3917075111dec2e824b92efaf34bf466 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Thu, 22 Jan 2026 21:50:32 +0530 Subject: [PATCH 069/254] #46 Sound mode button remap --- .../services/handlers/ButtonRemapHandler.kt | 9 ++++ .../services/handlers/SoundModeHandler.kt | 44 +++++++++++++++++++ .../services/tiles/SoundModeTileService.kt | 40 +++-------------- .../configs/ButtonRemapSettingsUI.kt | 6 +++ app/src/main/res/values/strings.xml | 1 + 5 files changed, 65 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/com/sameerasw/essentials/services/handlers/SoundModeHandler.kt diff --git a/app/src/main/java/com/sameerasw/essentials/services/handlers/ButtonRemapHandler.kt b/app/src/main/java/com/sameerasw/essentials/services/handlers/ButtonRemapHandler.kt index 03b2cf65..e7571e57 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/handlers/ButtonRemapHandler.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/handlers/ButtonRemapHandler.kt @@ -12,6 +12,7 @@ import android.util.Log import android.view.KeyEvent import android.os.VibratorManager import android.os.Vibrator +import android.app.NotificationManager import com.sameerasw.essentials.domain.HapticFeedbackType import com.sameerasw.essentials.utils.performHapticFeedback import com.sameerasw.essentials.utils.ShizukuUtils @@ -21,6 +22,7 @@ class ButtonRemapHandler( private val service: AccessibilityService, private val flashlightHandler: FlashlightHandler ) { + private val soundModeHandler = SoundModeHandler(service) private val handler = Handler(Looper.getMainLooper()) private var isLongPressTriggered: Boolean = false private var lastPressedKeyCode: Int = -1 @@ -182,7 +184,14 @@ class ButtonRemapHandler( "Toggle mute" -> toggleRingerMode(AudioManager.RINGER_MODE_SILENT) "AI assistant" -> launchAssistant() "Take screenshot" -> takeScreenshot() + "Cycle sound modes" -> cycleSoundModes() } + + } + + private fun cycleSoundModes() { + soundModeHandler.cycleNextMode() + triggerHapticFeedback() } private fun takeScreenshot() { diff --git a/app/src/main/java/com/sameerasw/essentials/services/handlers/SoundModeHandler.kt b/app/src/main/java/com/sameerasw/essentials/services/handlers/SoundModeHandler.kt new file mode 100644 index 00000000..6196c2ec --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/services/handlers/SoundModeHandler.kt @@ -0,0 +1,44 @@ +package com.sameerasw.essentials.services.handlers + +import android.app.NotificationManager +import android.content.Context +import android.media.AudioManager +import com.sameerasw.essentials.utils.HapticUtil + +class SoundModeHandler(private val context: Context) { + + fun cycleNextMode(): Int? { + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + if (!notificationManager.isNotificationPolicyAccessGranted) { + return null + } + + val prefs = context.getSharedPreferences("essentials_prefs", Context.MODE_PRIVATE) + val defaultOrder = listOf("Sound", "Vibrate", "Silent") + val orderString = prefs.getString("sound_mode_order", defaultOrder.joinToString(",")) ?: defaultOrder.joinToString(",") + val order = orderString.split(",") + + val currentMode = when (audioManager.ringerMode) { + AudioManager.RINGER_MODE_NORMAL -> "Sound" + AudioManager.RINGER_MODE_VIBRATE -> "Vibrate" + AudioManager.RINGER_MODE_SILENT -> "Silent" + else -> "Sound" + } + + val currentIndex = order.indexOf(currentMode) + val nextIndex = (currentIndex + 1) % order.size + val nextMode = order[nextIndex] + + val nextRingerMode = when (nextMode) { + "Sound" -> AudioManager.RINGER_MODE_NORMAL + "Vibrate" -> AudioManager.RINGER_MODE_VIBRATE + "Silent" -> AudioManager.RINGER_MODE_SILENT + else -> AudioManager.RINGER_MODE_NORMAL + } + + audioManager.ringerMode = nextRingerMode + return nextRingerMode + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/services/tiles/SoundModeTileService.kt b/app/src/main/java/com/sameerasw/essentials/services/tiles/SoundModeTileService.kt index d710f864..2292aed2 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/tiles/SoundModeTileService.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/tiles/SoundModeTileService.kt @@ -11,6 +11,7 @@ import android.os.Build import android.service.quicksettings.Tile import android.service.quicksettings.TileService import com.sameerasw.essentials.R +import com.sameerasw.essentials.services.handlers.SoundModeHandler import com.sameerasw.essentials.utils.HapticUtil class SoundModeTileService : TileService() { @@ -78,42 +79,11 @@ class SoundModeTileService : TileService() { override fun onClick() { super.onClick() - val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager - if (!notificationManager.isNotificationPolicyAccessGranted) { - return - } - - val prefs = getSharedPreferences("essentials_prefs", MODE_PRIVATE) - val defaultOrder = listOf("Sound", "Vibrate", "Silent") - val orderString = prefs.getString("sound_mode_order", defaultOrder.joinToString(",")) ?: defaultOrder.joinToString(",") - val order = orderString.split(",") - - val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager - - val currentMode = when (audioManager.ringerMode) { - AudioManager.RINGER_MODE_NORMAL -> "Sound" - AudioManager.RINGER_MODE_VIBRATE -> "Vibrate" - AudioManager.RINGER_MODE_SILENT -> "Silent" - else -> "Sound" + val nextRingerMode = SoundModeHandler(this).cycleNextMode() + if (nextRingerMode != null) { + latestAudioStateUpdate = nextRingerMode + updateSoundTile() } - - val currentIndex = order.indexOf(currentMode) - val nextIndex = (currentIndex + 1) % order.size - val nextMode = order[nextIndex] - - val nextRingerMode = when (nextMode) { - "Sound" -> AudioManager.RINGER_MODE_NORMAL - "Vibrate" -> AudioManager.RINGER_MODE_VIBRATE - "Silent" -> AudioManager.RINGER_MODE_SILENT - else -> AudioManager.RINGER_MODE_NORMAL - } - - audioManager.ringerMode = nextRingerMode - HapticUtil.performHapticForService(this) - - latestAudioStateUpdate = nextRingerMode - - updateSoundTile() } override fun onStartListening() { diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt index 269cef05..fe8d226f 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt @@ -391,6 +391,12 @@ fun ButtonRemapSettingsUI( onClick = { onActionSelected("AI assistant") }, iconRes = R.drawable.rounded_bubble_chart_24, ) + RemapActionItem( + title = stringResource(R.string.action_cycle_sound_modes), + isSelected = currentAction == "Cycle sound modes", + onClick = { onActionSelected("Cycle sound modes") }, + iconRes = R.drawable.rounded_volume_up_24, + ) if (selectedScreenTab == 1) { RemapActionItem( title = stringResource(R.string.action_take_screenshot), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0dc81050..9e97cf71 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,6 +53,7 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 91d5154b47ee7309cde4088678633021b26af7bc Mon Sep 17 00:00:00 2001 From: sameerasw Date: Thu, 22 Jan 2026 21:58:16 +0530 Subject: [PATCH 070/254] #46 Toggle media volume for button remap --- .../services/handlers/ButtonRemapHandler.kt | 19 ++++++++++++++++++- .../configs/ButtonRemapSettingsUI.kt | 6 ++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/sameerasw/essentials/services/handlers/ButtonRemapHandler.kt b/app/src/main/java/com/sameerasw/essentials/services/handlers/ButtonRemapHandler.kt index e7571e57..3b8db0d0 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/handlers/ButtonRemapHandler.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/handlers/ButtonRemapHandler.kt @@ -185,8 +185,8 @@ class ButtonRemapHandler( "AI assistant" -> launchAssistant() "Take screenshot" -> takeScreenshot() "Cycle sound modes" -> cycleSoundModes() + "Toggle media volume" -> toggleMediaVolume() } - } private fun cycleSoundModes() { @@ -210,6 +210,23 @@ class ButtonRemapHandler( triggerHapticFeedback() } + private fun toggleMediaVolume() { + val am = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val currentVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC) + val prefs = service.getSharedPreferences("essentials_prefs", Context.MODE_PRIVATE) + + if (currentVolume > 0) { + // Mute and save current volume + prefs.edit().putInt("last_media_volume", currentVolume).apply() + am.setStreamVolume(AudioManager.STREAM_MUSIC, 0, AudioManager.FLAG_SHOW_UI) + } else { + // Restore last known volume or default to mid-range + val lastVolume = prefs.getInt("last_media_volume", am.getStreamMaxVolume(AudioManager.STREAM_MUSIC) / 2) + am.setStreamVolume(AudioManager.STREAM_MUSIC, lastVolume, AudioManager.FLAG_SHOW_UI) + } + triggerHapticFeedback() + } + private fun toggleRingerMode(targetMode: Int) { val am = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager val currentMode = am.ringerMode diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt index fe8d226f..b4be3669 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/ButtonRemapSettingsUI.kt @@ -391,6 +391,12 @@ fun ButtonRemapSettingsUI( onClick = { onActionSelected("AI assistant") }, iconRes = R.drawable.rounded_bubble_chart_24, ) + RemapActionItem( + title = stringResource(R.string.action_toggle_media_volume), + isSelected = currentAction == "Toggle media volume", + onClick = { onActionSelected("Toggle media volume") }, + iconRes = R.drawable.rounded_volume_off_24, + ) RemapActionItem( title = stringResource(R.string.action_cycle_sound_modes), isSelected = currentAction == "Cycle sound modes", diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9e97cf71..e31fa404 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,6 +54,7 @@ AI assistant Take screenshot Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 3cdc37a2a094bd1760c60a667b7be2424566a531 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:13 +0530 Subject: [PATCH 071/254] Update source file strings.xml --- app/src/main/res/values/strings.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e31fa404..0dc81050 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,8 +53,6 @@ Toggle mute AI assistant Take screenshot - Cycle sound modes - Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 2b45c6826b91292c65d2965d3b55d7e97a707743 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:30 +0530 Subject: [PATCH 072/254] New translations strings.xml (Romanian) --- app/src/main/res/values-ro/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 38b81a1b..b79afec0 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 2b94c3d449e54f3104cf3c123a01653645df946e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:32 +0530 Subject: [PATCH 073/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 660e0de1..5d3a6114 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -52,6 +52,8 @@ Activer le mode silencieux Assistant IA Prendre une capture d\'écran + Cycle sound modes + Toggle media volume Quand l\'écran est éteint, appuyez longuement sur le bouton sélectionné pour déclencher son action assignée. Sur les appareils Pixel, cette action ne se déclenche que si le Always-On Display est activé à cause de limitations systèmes. Quand l\'écran est allumé, appuyez longuement sur le bouton sélectionné pour déclencher son action assignée. Intensité de la lampe-torche @@ -814,7 +816,7 @@ %1$d m %1$.1f km Alarme de trajet active - %1$s restant (%2$d%%) + %1$s restant Progrès du trajet Affiche la distance de la destination en temps réel Destination à proximité From 5577c4287354861621ac65d30f486e9f007b4748 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:33 +0530 Subject: [PATCH 074/254] New translations strings.xml (Spanish) --- app/src/main/res/values-es/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 0969c667c2828fd80fb9de8932c75f06043d144f Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:34 +0530 Subject: [PATCH 075/254] New translations strings.xml (Afrikaans) --- app/src/main/res/values-af/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From e9c850acda56d6ae172d0a71f27bfdcc73fa69cf Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:36 +0530 Subject: [PATCH 076/254] New translations strings.xml (Arabic) --- app/src/main/res/values-ar/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index fc91c91d..eb9c50cf 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 1de32131330b5135299ab3683175973d7599c79b Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:37 +0530 Subject: [PATCH 077/254] New translations strings.xml (Catalan) --- app/src/main/res/values-ca/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From db2696dc86269f912a170b8a4a3c1432a8376007 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:38 +0530 Subject: [PATCH 078/254] New translations strings.xml (Czech) --- app/src/main/res/values-cs/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 3eccfb8420fe448829a5ae779d625306c27de664 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:39 +0530 Subject: [PATCH 079/254] New translations strings.xml (Danish) --- app/src/main/res/values-da/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 5c5e93d1572931bf3c59936eae67de42a1485dad Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:40 +0530 Subject: [PATCH 080/254] New translations strings.xml (German) --- app/src/main/res/values-de/strings.xml | 140 +++++++++++++------------ 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c0243ba2..3740b3a9 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -52,6 +52,8 @@ Stummschaltung umschalten KI-Assistent Bildschirmfoto machen + Cycle sound modes + Toggle media volume Wenn der Bildschirm ausgeschaltet ist, kann durch langes Drücken der ausgewählten Taste die zugehörige Aktion ausgelöst werden. Auf Pixel-Geräten wird diese Aktion aufgrund von Systembeschränkungen nur ausgelöst, wenn das Always-On-Display aktiviert ist. Wenn der Bildschirm angeschaltet ist, kann durch langes Drücken der ausgewählten Taste die zugehörige Aktion ausgelöst werden. Taschenlampenintensität @@ -73,22 +75,22 @@ Benachrichtigungen senden Erlaubt es der App Benachrichtigungen zu zeigen Berechtigung gewähren - Caffeinate Active + Koffein aktiv Aktiv - Screen is being kept awake - Ignore battery optimization - Recommended for reliable service on some devices - Abort with screen off - Timeout Presets - Select available durations for QS tile + Bildschirm wird wach gehalten + Ignoriere Batterie-Optimierungen + Empfohlen für zuverlässigen Betrieb auf einigen Geräten + Abbruch bei ausgeschaltetem Bildschirm + Zeitvorlagen + Wähle eine Dauer für die Schnelleinstellungskachel 5min 10min 30min 1std - Starting in %1$ds… - %1$s remaining - Persistent notification for Caffeinate + Startet in %1$ds… + %1$s verbleibend + Dauerhafte Benachrichtigung für Koffein Aktiviere dynamisches Nachtlicht Apps, mit deaktiviertem Nachtlicht @@ -166,7 +168,7 @@ Apps einfrieren Taschenlampenimpuls Wach bleiben - Essentials Keyboard + Essentials-Tastatur Englisch (US) Aktiv Inaktiv @@ -272,10 +274,10 @@ Kachel in den Schnelleinstellungen zum Umschalten des Wachbleibens Bluetooth-Geräte anzeigen Zeige den Akkustand von verbundenen Bluetooth-Geräten - Limit max devices - Adjust max devices visible in widget - Widget background - Show widget background + Max. Geräteanzahl begrenzen + Anzahl der im Widget angezeigten Geräte anpassen + Widget-Hintergrund + Widget-Hintergrund anzeigen Auslöser-Automatisierung Eine Aktion planen, die bei einer Beobachtung ausgelöst wird @@ -308,8 +310,8 @@ Verstecke mobile Daten in bestimmten Modi Alle Icons zurücksetzen Sichtbarkeit der Statusleisten-Icons auf Standard zurücksetzen - Abort Caffeinate with screen off - Automatically turn off Caffeinate when manually locking the device + Koffein bei ausgeschaltetem Bildschirm abbrechen + Koffein automatisch deaktivieren, wenn das Gerät manuell gesperrt wird Lichtstil Wählen Sie zwischen Streifeneffekt, Leuchteffekt, Dreheffekt und mehr Eckradius @@ -396,7 +398,7 @@ Benötigt für Statusleisten-Symbole und Sicherheit bei Bildschirmsperre Benötigt, um das Nachtlicht zu aktivieren. Über ADB oder Root gewähren. Systemeinstellungen ändern - Required to toggle Adaptive Brightness and other system settings + Benötigt, zum Umschalten der adaptiven Helligkeit und anderer Systemeinstellungen Overlay-Berechtigung Benötigt, um das Benachrichtigungslicht auf dem Bildschirm anzuzeigen Geräteadministrator @@ -416,10 +418,10 @@ Deaktivierte Apps Do-It-Yourself - Keyboard Setup - Enable in settings - Switch to Essentials - Keyboard is active and ready! + Tastatur-Einrichtung + In den Einstellungen aktivieren + Wechsel zu Essentials + Tastatur ist aktiv und bereit! Aktiviert Deaktiviert Adaptive Helligkeit @@ -464,7 +466,7 @@ Diese Aktion erfordert Shizuku oder einen Root-Zugang um den Hintergrund zu dimmen. Auslöser auswählen App - Automate based on open app + Automatisierung basierend auf geöffneter App Zustand auswählen Aktion auswählen Eintrittsaktion @@ -478,14 +480,14 @@ Automatisierungsdienst Automatisierungen aktiv Überwachung von Systemereignissen für deine Automatisierungen - Device Effects - Disable Device Effects - Control system-level effects like grayscale, AOD suppression, wallpaper dimming, and night mode. - Grayscale - Suppress Ambient Display - Dim Wallpaper - Night Mode - This feature requires Android 15 or higher. + Geräteeffekte + Deaktiviere Geräteeffekte + Steuer Effekte auf Systemebene wie Graustufen, AOD-Unterdrückung, Hintergrundbild-Dimmung und Nachtmodus. + Graustufen + Ambient Display unterdrücken + Hintergrund dimmen + Nachtmodus + Für diese Funktion ist Android 15 oder höher erforderlich. Aktiviert Deaktiviert @@ -540,11 +542,11 @@ icon - keyboard - height - padding - haptic - input + tastatur + höhe + abstand + haptik + eingabe sichtbar @@ -614,9 +616,9 @@ warten zeitüberschreitung - Always dark theme - Pitch black theme - Clipboard History + Immer dunkles Design + Tiefschwarzes Design + Zwischenablage-Verlauf liste wähler @@ -825,35 +827,35 @@ Anstelle von Shizuku Root-Zugang nicht verfügbar. Bitte überprüfe deinen Root-Manager. - Keyboard - Keys - Customize layout and behavior - Keyboard Height - Adjust the total vertical size of the keyboard - Bottom Padding - Add space below the keyboard - Haptic Feedback - Vibrate on key press - Test the keyboard - Test the keyboard - Keyboard Height - Bottom Padding - Haptic Feedback - Key Roundness - Key Roundness - Adjust the corner radius of keys - Move functions to bottom - Functions side padding - Haptic feedback strength - Keyboard shape - Round - Flat - Inverse + Tastatur + Tasten + Layout und Funktionalität anpassen + Tastaturhöhe + Passe die vertikale Gesamtgröße der Tastatur an + Unterer Abstand + Platz unterhalb der Tastatur hinzufügen + Haptisches Feedback + Bei Tastendruck vibrieren + Teste die Tastatur + Teste die Tastatur + Tastaturhöhe + Unterer Abstand + Haptisches Feedback + Tastenrundheit + Tastenrundheit + Eckradius der Tasten anpassen + Funktionen nach unten verschieben + Seitenabstand Funktionen + Stärke des haptischen Feedbacks + Tastaturform + Rund + Flach + Invers Akkus - Monitor your device battery levels - Battery Status - Connect to AirSync - Display battery from your connected mac device in AirSync - Download AirSync App - Required for Mac battery sync + Überwache den Akkustand deines Geräts + Batteriestatus + Mit AirSync verbinden + Zeige den Akku deines verbundenen Mac-Geräts in AirSync an + AirSync-App herunterladen + Benötigt für die Mac-Akkusynchronisierung From 75294465736b972905ac52c6110c9298b93519a3 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:42 +0530 Subject: [PATCH 081/254] New translations strings.xml (Greek) --- app/src/main/res/values-el/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From a07d37a1c374036d19f8275aa2f5f504ee57d87f Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:43 +0530 Subject: [PATCH 082/254] New translations strings.xml (Finnish) --- app/src/main/res/values-fi/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From db2afac7772f6eca60739368bc873ab47a5b5f84 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:44 +0530 Subject: [PATCH 083/254] New translations strings.xml (Hebrew) --- app/src/main/res/values-he/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 2335a693614844b4ee848aa47e25c8a43329edb0 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:45 +0530 Subject: [PATCH 084/254] New translations strings.xml (Hungarian) --- app/src/main/res/values-hu/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 8b5e4075eaca5604aedbbabfaa72151687a967d5 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:46 +0530 Subject: [PATCH 085/254] New translations strings.xml (Italian) --- app/src/main/res/values-it/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 6868b6ac..e3b2d658 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -53,6 +53,8 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Attiva/Disattiva il silenzioso Assistente AI Fai uno screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. Quando lo schermo è acceso, tieni premuto il tasto selezionato per avviare l\'azione selezionata. Intensità Torcia From 1942c38937307dd66294eeb263133f004d076bc4 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:48 +0530 Subject: [PATCH 086/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 56014c6d..a452ea61 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -52,6 +52,8 @@ ミュート アシスタント スクリーンショット + Cycle sound modes + Toggle media volume 画面がオフのときに、選択したボタンを長押しすると、割り当てられたアクションが実行されます。Pixelデバイスでは、システムの制限により、このアクションはAODがオンの場合にのみ実行されます。 画面がオンのときに、選択したボタンを長押しすると、割り当てられたアクションがトリガーされます。 フラッシュライトの明るさ From 7e5b114d06c9459f687e9312fc12090d289ed8ba Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:49 +0530 Subject: [PATCH 087/254] New translations strings.xml (Korean) --- app/src/main/res/values-ko/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 11c0dafccf4843c2a52605e18e712fae0a8b1f96 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:50 +0530 Subject: [PATCH 088/254] New translations strings.xml (Dutch) --- app/src/main/res/values-nl/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From f7e66c685e965acb96cb91177f77478a524553d2 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:51 +0530 Subject: [PATCH 089/254] New translations strings.xml (Norwegian) --- app/src/main/res/values-no/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From c2c00be0311544a50abd0d803198a2f59e5fa0eb Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:53 +0530 Subject: [PATCH 090/254] New translations strings.xml (Polish) --- app/src/main/res/values-pl/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 5113068aedda20bf91de5d6eb51c3aed57044e6a Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:54 +0530 Subject: [PATCH 091/254] New translations strings.xml (Portuguese) --- app/src/main/res/values-pt/strings.xml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 423e8d42..e80c396f 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity @@ -379,7 +381,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -762,7 +764,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -781,14 +783,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -813,7 +815,7 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active + Travel alarm active %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination From 7d8dd9a91f89d3a970a06a6d0fb84071c548f5ab Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:55 +0530 Subject: [PATCH 092/254] New translations strings.xml (Russian) --- app/src/main/res/values-ru/strings.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 679e7f5a..3c9073e3 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -52,6 +52,8 @@ Без звука AI ассистент Сделать снимок экрана + Cycle sound modes + Toggle media volume Когда экран выключен, нажмите и удерживайте выбранную кнопку, чтобы активировать назначенное действие. На устройствах Pixel это действие запускается только при включенном AOD из-за системных ограничений. Когда экран включен, нажмите и удерживайте выбранную кнопку, чтобы активировать назначенное ей действие. Яркость фонарика @@ -79,7 +81,7 @@ Игнорировать оптимизацию батареи Рекомендуется для надежной работы на некоторых устройствах Прервать если экран выключен - Интервалы работы + Предустановки времени Выберите доступные интервалы для плитки быстрого доступа 10м @@ -181,7 +183,7 @@ ⚠️ ВНИМАНИЕ Эта функция не является надежной. В некоторых случаях пользователь все еще может взаимодействовать с плиткой. \nТакже имейте в виду, что Android всегда разрешает принудительную перезагрузку, а Пиксели всегда позволяют отключить устройство с экрана блокировки. Обязательно удалите плитку \"Режим полета\" из \"быстрых настроек\", так как это невозможно предотвратить, поскольку она не открывает диалоговое окно. - Если эта функция включена, панель быстрых настроек будет немедленно закрыта, а устройство заблокировано, если кто-либо попытается взаимодействовать с интернет-плитками, пока устройство заблокировано. \n\nЭто также отключит биометрическую разблокировку, чтобы предотвратить дальнейший несанкционированный доступ. Во время блокировки анимации будут замедлены до 0.1x, что еще больше усложнит взаимодействие с устройством. + Если эта функция включена, панель быстрых настроек будет немедленно закрыта, а устройство заблокировано, если кто-либо попытается взаимодействовать с интернет-плитками, пока устройство заблокировано. \n\nЭто также отключит биометрическую разблокировку, чтобы предотвратить дальнейший несанкционированный доступ. Во время блокировки масштаб анимации будет уменьшен до 0.1x, что еще больше усложнит взаимодействие с устройством. Изменить порядок режимов Долгое нажатие для переключения From 5fb5b58a3f05d188d14326f5af865e55aa2a8a33 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:56 +0530 Subject: [PATCH 093/254] New translations strings.xml (Serbian (Cyrillic)) --- app/src/main/res/values-sr/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From cadf5390c1311894e8bd8a7be4765dab705c4d3e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:57 +0530 Subject: [PATCH 094/254] New translations strings.xml (Swedish) --- app/src/main/res/values-sv/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From fe4aeab6af5269a978941e64e368901e6bb67627 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:19:59 +0530 Subject: [PATCH 095/254] New translations strings.xml (Turkish) --- app/src/main/res/values-tr/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 5478249e03b399fb204cde976671892c8aa03b0c Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:20:00 +0530 Subject: [PATCH 096/254] New translations strings.xml (Ukrainian) --- app/src/main/res/values-uk/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 9b1f5780b7105bf01f20fb1db05cf6654e9d3004 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:20:02 +0530 Subject: [PATCH 097/254] New translations strings.xml (Chinese Simplified) --- app/src/main/res/values-zh/strings.xml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 423e8d42..e80c396f 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity @@ -379,7 +381,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -762,7 +764,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -781,14 +783,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -813,7 +815,7 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active + Travel alarm active %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination From 66f3fee0423e85a1dc264fda222aebce9a144e28 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:20:03 +0530 Subject: [PATCH 098/254] New translations strings.xml (Chinese Traditional) --- app/src/main/res/values-zh/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index e80c396f..7f89847d 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -381,7 +381,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -764,7 +764,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -783,14 +783,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -815,8 +815,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From 41dc2eb0681ddc0da6710ba406a7278dd697e58b Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:20:04 +0530 Subject: [PATCH 099/254] New translations strings.xml (English) --- app/src/main/res/values-en/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 7e979cad6cc3f4974ad958d23a3f6fe00ce65c54 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:20:06 +0530 Subject: [PATCH 100/254] New translations strings.xml (Vietnamese) --- app/src/main/res/values-vi/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 4896a88c5c1e8c1017e03d1dc2cd3561883cae05 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:20:07 +0530 Subject: [PATCH 101/254] New translations strings.xml (Portuguese, Brazilian) --- app/src/main/res/values-pt/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index e80c396f..7f89847d 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -381,7 +381,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -764,7 +764,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -783,14 +783,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -815,8 +815,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From 98bb5cf5af3172119d2e26c0077cf95a9b5a843e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:20:08 +0530 Subject: [PATCH 102/254] New translations strings.xml (Sinhala) --- app/src/main/res/values-si/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 84391ed526a0e283ba8d474d67a31fca9ec9ec23 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 22:20:10 +0530 Subject: [PATCH 103/254] New translations strings.xml (Acholi) --- app/src/main/res/values-ach/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ach/strings.xml b/app/src/main/res/values-ach/strings.xml index 423e8d42..819ca177 100644 --- a/app/src/main/res/values-ach/strings.xml +++ b/app/src/main/res/values-ach/strings.xml @@ -52,6 +52,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From dd169b1ac4c5cfbd84d50ab88cb547b13ea5c58e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Thu, 22 Jan 2026 23:18:00 +0530 Subject: [PATCH 104/254] Update source file strings.xml --- app/src/main/res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0dc81050..e31fa404 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,6 +53,8 @@ Toggle mute AI assistant Take screenshot + Cycle sound modes + Toggle media volume When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. When the screen is on, long-press the selected button to trigger its assigned action. Flashlight Intensity From 73e8291ba1460e3af86386461b597c353054351f Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 00:13:38 +0530 Subject: [PATCH 105/254] Fix #145 System notification detection is no longer language specific --- .../data/repository/SettingsRepository.kt | 48 +++++++++- .../essentials/domain/model/SnoozeChannel.kt | 7 ++ .../services/NotificationListener.kt | 93 +++++++++++++------ .../configs/SnoozeNotificationsSettingsUI.kt | 59 +++++------- .../essentials/viewmodels/MainViewModel.kt | 42 +++++---- app/src/main/res/values/strings.xml | 7 +- 6 files changed, 167 insertions(+), 89 deletions(-) create mode 100644 app/src/main/java/com/sameerasw/essentials/domain/model/SnoozeChannel.kt diff --git a/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt b/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt index 24f161ab..b34f56e8 100644 --- a/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt @@ -61,9 +61,8 @@ class SettingsRepository(private val context: Context) { const val KEY_DYNAMIC_NIGHT_LIGHT_ENABLED = "dynamic_night_light_enabled" const val KEY_DYNAMIC_NIGHT_LIGHT_SELECTED_APPS = "dynamic_night_light_selected_apps" - const val KEY_SNOOZE_DEBUGGING_ENABLED = "snooze_debugging_enabled" - const val KEY_SNOOZE_FILE_TRANSFER_ENABLED = "snooze_file_transfer_enabled" - const val KEY_SNOOZE_CHARGING_ENABLED = "snooze_charging_enabled" + const val KEY_SNOOZE_DISCOVERED_CHANNELS = "snooze_discovered_channels" + const val KEY_SNOOZE_BLOCKED_CHANNELS = "snooze_blocked_channels" const val KEY_FLASHLIGHT_ALWAYS_TURN_OFF_ENABLED = "flashlight_always_turn_off_enabled" const val KEY_FLASHLIGHT_FADE_ENABLED = "flashlight_fade_enabled" @@ -299,6 +298,45 @@ class SettingsRepository(private val context: Context) { saveAppSelection(key, current) } } + + // Snooze Notifications Helper + fun loadSnoozeDiscoveredChannels(): List { + val json = prefs.getString(KEY_SNOOZE_DISCOVERED_CHANNELS, null) + return if (json != null) { + val type = object : TypeToken>() {}.type + try { + gson.fromJson(json, type) ?: emptyList() + } catch (e: Exception) { + emptyList() + } + } else { + emptyList() + } + } + + fun saveSnoozeDiscoveredChannels(channels: List) { + val json = gson.toJson(channels) + putString(KEY_SNOOZE_DISCOVERED_CHANNELS, json) + } + + fun loadSnoozeBlockedChannels(): Set { + val json = prefs.getString(KEY_SNOOZE_BLOCKED_CHANNELS, null) + return if (json != null) { + val type = object : TypeToken>() {}.type + try { + gson.fromJson(json, type) ?: emptySet() + } catch (e: Exception) { + emptySet() + } + } else { + emptySet() + } + } + + fun saveSnoozeBlockedChannels(blockedChannels: Set) { + val json = gson.toJson(blockedChannels) + putString(KEY_SNOOZE_BLOCKED_CHANNELS, json) + } // Config Export/Import fun getAllConfigsAsJsonString(): String { @@ -312,7 +350,9 @@ class SettingsRepository(private val context: Context) { p.all.forEach { (key, value) -> // Skip app lists as requested, and stale data - if (key.endsWith("_selected_apps") || key == "freeze_auto_excluded_apps" || key.startsWith("mac_battery_") || key == "airsync_mac_connected") { + if (key.endsWith("_selected_apps") || key == "freeze_auto_excluded_apps" || + key.startsWith("mac_battery_") || key == "airsync_mac_connected" || + key == KEY_SNOOZE_DISCOVERED_CHANNELS) { return@forEach } diff --git a/app/src/main/java/com/sameerasw/essentials/domain/model/SnoozeChannel.kt b/app/src/main/java/com/sameerasw/essentials/domain/model/SnoozeChannel.kt new file mode 100644 index 00000000..8ece27be --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/domain/model/SnoozeChannel.kt @@ -0,0 +1,7 @@ +package com.sameerasw.essentials.domain.model + +data class SnoozeChannel( + val id: String, + val name: String, + val isBlocked: Boolean = false +) diff --git a/app/src/main/java/com/sameerasw/essentials/services/NotificationListener.kt b/app/src/main/java/com/sameerasw/essentials/services/NotificationListener.kt index 0c1aab83..e555c542 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/NotificationListener.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/NotificationListener.kt @@ -17,6 +17,52 @@ import com.sameerasw.essentials.services.tiles.ScreenOffAccessibilityService import com.sameerasw.essentials.utils.AppUtil class NotificationListener : NotificationListenerService() { + + override fun onListenerConnected() { + super.onListenerConnected() + try { + // Initial discovery from active notifications + activeNotifications?.forEach { sbn -> + val isSystem = sbn.packageName == "android" || sbn.packageName == "com.android.systemui" + if (isSystem) { + discoverSystemChannel(sbn.packageName, sbn.notification.channelId, sbn.user) + } + } + } catch (_: Exception) {} + } + + private fun discoverSystemChannel(packageName: String, channelId: String?, userHandle: android.os.UserHandle) { + if (channelId.isNullOrBlank()) return + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + try { + val prefs = applicationContext.getSharedPreferences("essentials_prefs", Context.MODE_PRIVATE) + val discoveredJson = prefs.getString("snooze_discovered_channels", null) + val gson = com.google.gson.Gson() + val type = object : com.google.gson.reflect.TypeToken>() {}.type + val discoveredChannels: MutableList = if (discoveredJson != null) { + try { gson.fromJson(discoveredJson, type) ?: mutableListOf() } catch (_: Exception) { mutableListOf() } + } else mutableListOf() + + if (discoveredChannels.none { it.id == channelId }) { + var foundName: String? = null + try { + val channels = getNotificationChannels(packageName, userHandle) + val channel = channels.find { it.id == channelId } + foundName = channel?.name?.toString() + } catch (_: Exception) {} + + val name = if (!foundName.isNullOrBlank()) foundName + else channelId.replace("_", " ").lowercase().replaceFirstChar { it.uppercase() } + + val finalName = if (packageName == "android") name else "[$packageName] $name" + + discoveredChannels.add(com.sameerasw.essentials.domain.model.SnoozeChannel(channelId, finalName)) + prefs.edit().putString("snooze_discovered_channels", gson.toJson(discoveredChannels)).apply() + } + } catch (_: Exception) {} + } + } @RequiresApi(Build.VERSION_CODES.O) override fun onNotificationPosted(sbn: StatusBarNotification) { @@ -34,38 +80,29 @@ class NotificationListener : NotificationListenerService() { // Handle Snooze System Notifications try { - val packageName = sbn.packageName - val isSystem = packageName == "android" || packageName == "com.android.systemui" - + val pkg = sbn.packageName + val isSystem = pkg == "android" || pkg.startsWith("com.android.") || pkg == "com.google.android.gms" + if (isSystem) { - val extras = sbn.notification.extras - val title = extras.getString(Notification.EXTRA_TITLE) ?: "" - val text = extras.getString(Notification.EXTRA_TEXT) ?: "" - val content = "$title $text" - - // 1. Debugging - if (prefs.getBoolean("snooze_debugging_enabled", false)) { - val debugRegex = Regex("(?i).*(usb|wireless)\\s*debugging\\s*connected.*") - if (debugRegex.containsMatchIn(content)) { + val channelId = sbn.notification.channelId + + // 1. Discovery + discoverSystemChannel(pkg, channelId, sbn.user) + + // 2. Snoozing + if (channelId != null) { + val blockedChannelsJson = prefs.getString("snooze_blocked_channels", null) + val blockedChannels: Set = if (blockedChannelsJson != null) { + try { + val type = object : com.google.gson.reflect.TypeToken>() {}.type + com.google.gson.Gson().fromJson(blockedChannelsJson, type) ?: emptySet() + } catch (_: Exception) { emptySet() } + } else emptySet() + + if (blockedChannels.contains(channelId)) { snoozeNotification(sbn.key, 24 * 60 * 60 * 1000L) // Snooze for 24 hours } } - - // 2. File Transfer - if (prefs.getBoolean("snooze_file_transfer_enabled", false)) { - val fileTransferRegex = Regex("(?i).*usb\\s*file\\s*transfer.*") - if (fileTransferRegex.containsMatchIn(content)) { - snoozeNotification(sbn.key, 24 * 60 * 60 * 1000L) - } - } - - // 3. Charging - if (prefs.getBoolean("snooze_charging_enabled", false)) { - val chargingRegex = Regex("(?i).*charging\\s*this\\s*device.*") - if (chargingRegex.containsMatchIn(content)) { - snoozeNotification(sbn.key, 24 * 60 * 60 * 1000L) - } - } } } catch (_: Exception) { // Safe to ignore diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/SnoozeNotificationsSettingsUI.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/SnoozeNotificationsSettingsUI.kt index f6c73582..b4c7db8c 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/SnoozeNotificationsSettingsUI.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/SnoozeNotificationsSettingsUI.kt @@ -38,41 +38,32 @@ fun SnoozeNotificationsSettingsUI( spacing = 2.dp, cornerRadius = 24.dp ) { - // Debugging - IconToggleItem( - iconRes = R.drawable.rounded_adb_24, - title = stringResource(R.string.search_snooze_debug_title), - isChecked = viewModel.isSnoozeDebuggingEnabled.value, - onCheckedChange = { checked -> - HapticUtil.performVirtualKeyHaptic(view) - viewModel.setSnoozeDebuggingEnabled(checked, context) - }, - modifier = Modifier.highlight(highlightSetting == "snooze_debugging") - ) + viewModel.snoozeChannels.value.forEach { channel -> + IconToggleItem( + iconRes = when (channel.id) { + "DEVELOPER_OPTIONS" -> R.drawable.rounded_adb_24 + "USB_CONNECTION" -> R.drawable.rounded_usb_24 + "BATTERY" -> R.drawable.rounded_charger_24 + else -> R.drawable.rounded_notification_settings_24 + }, + title = channel.name, + isChecked = channel.isBlocked, + onCheckedChange = { checked -> + HapticUtil.performVirtualKeyHaptic(view) + viewModel.setSnoozeChannelBlocked(channel.id, checked, context) + }, + modifier = Modifier.highlight(highlightSetting == channel.id) + ) + } - // File Transfer - IconToggleItem( - iconRes = R.drawable.rounded_usb_24, - title = stringResource(R.string.search_snooze_file_title), - isChecked = viewModel.isSnoozeFileTransferEnabled.value, - onCheckedChange = { checked -> - HapticUtil.performVirtualKeyHaptic(view) - viewModel.setSnoozeFileTransferEnabled(checked, context) - }, - modifier = Modifier.highlight(highlightSetting == "snooze_file_transfer") - ) - - // Charging - IconToggleItem( - iconRes = R.drawable.rounded_charger_24, - title = stringResource(R.string.search_snooze_charge_title), - isChecked = viewModel.isSnoozeChargingEnabled.value, - onCheckedChange = { checked -> - HapticUtil.performVirtualKeyHaptic(view) - viewModel.setSnoozeChargingEnabled(checked, context) - }, - modifier = Modifier.highlight(highlightSetting == "snooze_charging") - ) + if (viewModel.snoozeChannels.value.isEmpty()) { + androidx.compose.material3.Text( + text = stringResource(R.string.snooze_no_channels_discovered), + modifier = Modifier.padding(16.dp), + style = androidx.compose.material3.MaterialTheme.typography.bodyMedium, + color = androidx.compose.material3.MaterialTheme.colorScheme.onSurfaceVariant + ) + } } } } diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt index a77a96c9..5ab3d931 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt @@ -72,9 +72,7 @@ class MainViewModel : ViewModel() { val volumeDownActionOn = mutableStateOf("None") val remapHapticType = mutableStateOf(HapticFeedbackType.DOUBLE) val isDynamicNightLightEnabled = mutableStateOf(false) - val isSnoozeDebuggingEnabled = mutableStateOf(false) - val isSnoozeFileTransferEnabled = mutableStateOf(false) - val isSnoozeChargingEnabled = mutableStateOf(false) + val snoozeChannels = mutableStateOf>(emptyList()) val isFlashlightAlwaysTurnOffEnabled = mutableStateOf(false) val isFlashlightFadeEnabled = mutableStateOf(false) val isFlashlightAdjustEnabled = mutableStateOf(false) @@ -159,6 +157,7 @@ class MainViewModel : ViewModel() { private var lastUpdateCheckTime: Long = 0 private lateinit var settingsRepository: SettingsRepository private lateinit var updateRepository: UpdateRepository + private var appContext: Context? = null private val preferenceChangeListener = android.content.SharedPreferences.OnSharedPreferenceChangeListener { _, key -> // We still use this listener for now, attached via Repository @@ -203,10 +202,14 @@ class MainViewModel : ViewModel() { SettingsRepository.KEY_MAC_BATTERY_LAST_UPDATED -> macBatteryLastUpdated.value = settingsRepository.getLong(key, 0L) SettingsRepository.KEY_AIRSYNC_MAC_CONNECTED -> isMacConnected.value = settingsRepository.getBoolean(key, false) SettingsRepository.KEY_BATTERY_WIDGET_MAX_DEVICES -> batteryWidgetMaxDevices.intValue = settingsRepository.getInt(key, 8) + SettingsRepository.KEY_SNOOZE_DISCOVERED_CHANNELS, SettingsRepository.KEY_SNOOZE_BLOCKED_CHANNELS -> { + appContext?.let { loadSnoozeChannels(it) } + } } } fun check(context: Context) { + appContext = context.applicationContext settingsRepository = SettingsRepository(context) updateRepository = UpdateRepository() @@ -304,9 +307,7 @@ class MainViewModel : ViewModel() { } isDynamicNightLightEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_DYNAMIC_NIGHT_LIGHT_ENABLED) - isSnoozeDebuggingEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_SNOOZE_DEBUGGING_ENABLED) - isSnoozeFileTransferEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_SNOOZE_FILE_TRANSFER_ENABLED) - isSnoozeChargingEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_SNOOZE_CHARGING_ENABLED) + loadSnoozeChannels(context) isFlashlightAlwaysTurnOffEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_FLASHLIGHT_ALWAYS_TURN_OFF_ENABLED) isFlashlightFadeEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_FLASHLIGHT_FADE_ENABLED) isFlashlightAdjustEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_FLASHLIGHT_ADJUST_INTENSITY_ENABLED) @@ -1143,19 +1144,26 @@ class MainViewModel : ViewModel() { } } - fun setSnoozeDebuggingEnabled(enabled: Boolean, context: Context) { - isSnoozeDebuggingEnabled.value = enabled - settingsRepository.putBoolean(SettingsRepository.KEY_SNOOZE_DEBUGGING_ENABLED, enabled) - } - - fun setSnoozeFileTransferEnabled(enabled: Boolean, context: Context) { - isSnoozeFileTransferEnabled.value = enabled - settingsRepository.putBoolean(SettingsRepository.KEY_SNOOZE_FILE_TRANSFER_ENABLED, enabled) + fun loadSnoozeChannels(context: Context) { + val discovered = settingsRepository.loadSnoozeDiscoveredChannels() + val blocked = settingsRepository.loadSnoozeBlockedChannels() + + val channels = discovered.map { channel -> + channel.copy(isBlocked = blocked.contains(channel.id)) + } + + snoozeChannels.value = channels.distinctBy { it.id }.sortedBy { it.name } } - fun setSnoozeChargingEnabled(enabled: Boolean, context: Context) { - isSnoozeChargingEnabled.value = enabled - settingsRepository.putBoolean(SettingsRepository.KEY_SNOOZE_CHARGING_ENABLED, enabled) + fun setSnoozeChannelBlocked(channelId: String, blocked: Boolean, context: Context) { + val currentBlocked = settingsRepository.loadSnoozeBlockedChannels().toMutableSet() + if (blocked) { + currentBlocked.add(channelId) + } else { + currentBlocked.remove(channelId) + } + settingsRepository.saveSnoozeBlockedChannels(currentBlocked) + loadSnoozeChannels(context) } fun setFlashlightAlwaysTurnOffEnabled(enabled: Boolean, context: Context) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e31fa404..947f7733 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -340,12 +340,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 8d40376c44658beee560ce5d0cd20f872365412a Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:02 +0530 Subject: [PATCH 106/254] New translations strings.xml (Romanian) --- app/src/main/res/values-ro/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index b79afec0..7096b11d 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 1603f3fed730295c068af945ee188df649e5c241 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:03 +0530 Subject: [PATCH 107/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 5d3a6114..cbf85337 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -322,12 +322,7 @@ Faire clignoter lentement la lampe-torche pour les nouvelles notifications Seulement quand le téléphone est vers le bas Impulsion de lampe torche seulement quand l\'appareil est vers le bas - Désactiver les notifications de débogage - Cacher les notifications de débogage ADB/USB persistantes - Désactiver la notification de transfert de fichiers - Cache les notifications de transfert de fichiers USB persistantes - Désactiver la notification de recharge - Cacher les notifications persistantes de recharge + No system channels discovered yet. They will appear here once detected. Flou d\'interface Basculer le flou d\'interface à l\'échelle du système Bulles From 448af85647b1f4ecc95d6a0ae36b4701d26ea10f Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:04 +0530 Subject: [PATCH 108/254] New translations strings.xml (Spanish) --- app/src/main/res/values-es/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From c2faf0fa0b34e2444265748ddb072ab5a77dce85 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:05 +0530 Subject: [PATCH 109/254] New translations strings.xml (Afrikaans) --- app/src/main/res/values-af/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 8dc25e42840572437ef797e56d7ef0db8245ac32 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:07 +0530 Subject: [PATCH 110/254] New translations strings.xml (Arabic) --- app/src/main/res/values-ar/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index eb9c50cf..f5cfaa71 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From e33a445dd77ababfba505d74e52ab26c927179cc Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:08 +0530 Subject: [PATCH 111/254] New translations strings.xml (Catalan) --- app/src/main/res/values-ca/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From a3e289070a6dadce8a9df375b263a64ef9b30aa7 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:09 +0530 Subject: [PATCH 112/254] New translations strings.xml (Czech) --- app/src/main/res/values-cs/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 7866bfb8fe42720493976a2d4c4a71dc67226f1c Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:11 +0530 Subject: [PATCH 113/254] New translations strings.xml (Danish) --- app/src/main/res/values-da/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From a19ede6e3a55f23c59e60fb33655c290b7604efc Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:12 +0530 Subject: [PATCH 114/254] New translations strings.xml (German) --- app/src/main/res/values-de/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3740b3a9..f0ad691b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -322,12 +322,7 @@ Langsames Blinken der Taschenlampe bei neuen Benachrichtigungen Nur, wenn das Handy umgedreht ist Die Taschenlampe blinkt nur, wenn das Gerät mit der Vorderseite nach unten liegt - Debugging-Benachrichtigungen deaktivieren - Persistente ADB/USB-Debugging-Benachrichtigungen ausblenden - Benachrichtigung zur Dateiübertragung deaktivieren - Persistente Benachrichtigungen zur USB-Dateiübertragung ausblenden - Lade-Benachrichtigung deaktivieren - Persistente Benachrichtigungen zum Aufladen ausblenden + No system channels discovered yet. They will appear here once detected. UI-Unschärfe Systemweite UI-Unschärfe umschalten Bubbles From b5de072184e5cdf636227541159bc225a02db9a3 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:13 +0530 Subject: [PATCH 115/254] New translations strings.xml (Greek) --- app/src/main/res/values-el/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 5fb69746443f3f0f9aca92192219e37b3640a9ea Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:14 +0530 Subject: [PATCH 116/254] New translations strings.xml (Finnish) --- app/src/main/res/values-fi/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From e41041274b7c7bc2baa2656d0db52c864b0cb9fe Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:16 +0530 Subject: [PATCH 117/254] New translations strings.xml (Hebrew) --- app/src/main/res/values-he/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From e4ea9e8ae8dd2f6fa84de11243c7060e5f99fdbd Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:17 +0530 Subject: [PATCH 118/254] New translations strings.xml (Hungarian) --- app/src/main/res/values-hu/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 20932178af5879b662ed9689f8be16416f0f46d7 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:18 +0530 Subject: [PATCH 119/254] New translations strings.xml (Italian) --- app/src/main/res/values-it/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index e3b2d658..96fa8a89 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -323,12 +323,7 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 5b4833b49a2cd01579739bcb7a5decac80e8043e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:19 +0530 Subject: [PATCH 120/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index a452ea61..e48b4487 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -322,12 +322,7 @@ 新しい通知が届いたらフラッシュライトをゆっくり点滅させる 下を向いているときのみ デバイスが下を向いているときのみフラッシュライトを点滅 - デバッグ通知を無効にする - 永続的なADB/USBデバッグの通知を無効にする - ファイル転送通知を無効にする - 永続的なUSBファイル転送通知を無効にする - 充電通知を無効にする - システムからの充電通知を無効にする + No system channels discovered yet. They will appear here once detected. UIブラー システム全体のUIぼかしを切り替える バブル From ba32d8ff374a57f1857f19032e68b991fe14c28a Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:20 +0530 Subject: [PATCH 121/254] New translations strings.xml (Korean) --- app/src/main/res/values-ko/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 36e040515a95f61331285a54ccd6a9da4d5efbce Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:22 +0530 Subject: [PATCH 122/254] New translations strings.xml (Dutch) --- app/src/main/res/values-nl/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 7d4add69702009e1d092ab9796b4130867cc7d19 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:23 +0530 Subject: [PATCH 123/254] New translations strings.xml (Norwegian) --- app/src/main/res/values-no/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 2dddffc967c913945e04e1c41f55e74c4fc9be9c Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:24 +0530 Subject: [PATCH 124/254] New translations strings.xml (Polish) --- app/src/main/res/values-pl/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 2d5ad53fdfdc8af22d64cebd2039084403065e0d Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:26 +0530 Subject: [PATCH 125/254] New translations strings.xml (Portuguese) --- app/src/main/res/values-pt/strings.xml | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 7f89847d..51466500 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles @@ -381,7 +376,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -764,7 +759,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -783,14 +778,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -815,8 +810,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining + Travel alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From 2571ee7293182c925d87672d439e26daf697d3e2 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:27 +0530 Subject: [PATCH 126/254] New translations strings.xml (Russian) --- app/src/main/res/values-ru/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 3c9073e3..dcc60f68 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From f89a1d24fba6f0562c299fe5f707b8a51da99317 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:28 +0530 Subject: [PATCH 127/254] New translations strings.xml (Serbian (Cyrillic)) --- app/src/main/res/values-sr/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From d6b85cecd46b5f44d588c7fe7aa6b48bccf79196 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:29 +0530 Subject: [PATCH 128/254] New translations strings.xml (Swedish) --- app/src/main/res/values-sv/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 9f6ded9449ec858da471870fa9969a1aab283666 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:31 +0530 Subject: [PATCH 129/254] New translations strings.xml (Turkish) --- app/src/main/res/values-tr/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 686ce17a630a4ea40e6004b11958aabe5e5c941c Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:32 +0530 Subject: [PATCH 130/254] New translations strings.xml (Ukrainian) --- app/src/main/res/values-uk/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From decd94f406ecefa4a00efd6c6fd88e02f8132b7e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:33 +0530 Subject: [PATCH 131/254] New translations strings.xml (Chinese Simplified) --- app/src/main/res/values-zh/strings.xml | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 7f89847d..51466500 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles @@ -381,7 +376,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -764,7 +759,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -783,14 +778,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -815,8 +810,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining + Travel alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From 92ccca04da1e805565630658f4c46ac17fc0ef69 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:35 +0530 Subject: [PATCH 132/254] New translations strings.xml (Chinese Traditional) --- app/src/main/res/values-zh/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 51466500..fe0f5b41 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -376,7 +376,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -759,7 +759,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -778,14 +778,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -810,8 +810,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From 2c1e613f608ad7f17295c03fe73325491b1c4cf2 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:36 +0530 Subject: [PATCH 133/254] New translations strings.xml (English) --- app/src/main/res/values-en/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From a7865de83818683aa6e0c2901e4d46eaf1a5c855 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:38 +0530 Subject: [PATCH 134/254] New translations strings.xml (Vietnamese) --- app/src/main/res/values-vi/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 66de9a1e18a3a6e8b851a7859cc8f74313119be1 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:39 +0530 Subject: [PATCH 135/254] New translations strings.xml (Portuguese, Brazilian) --- app/src/main/res/values-pt/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 51466500..fe0f5b41 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -376,7 +376,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -759,7 +759,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -778,14 +778,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -810,8 +810,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From 577e019dee4a0b0114fb0c34dcf90b8a8bdbd122 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:41 +0530 Subject: [PATCH 136/254] New translations strings.xml (Sinhala) --- app/src/main/res/values-si/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From bca15de34535821123d701828f5213c75171c9f7 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 00:40:42 +0530 Subject: [PATCH 137/254] New translations strings.xml (Acholi) --- app/src/main/res/values-ach/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values-ach/strings.xml b/app/src/main/res/values-ach/strings.xml index 819ca177..4b1e94b0 100644 --- a/app/src/main/res/values-ach/strings.xml +++ b/app/src/main/res/values-ach/strings.xml @@ -322,12 +322,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From cdd6fde71b50ca761b40a3a6ba5c673852871ddb Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 01:46:46 +0530 Subject: [PATCH 138/254] Update source file strings.xml --- app/src/main/res/values/strings.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e31fa404..947f7733 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -340,12 +340,7 @@ Slowly pulse flashlight for new notifications Only while facing down Pulse flashlight only when device is face down - Disable debugging notifications - Hide persistent ADB/USB debugging notifications - Disable file transfer notification - Hide persistent USB file transfer notifications - Disable charging notification - Hide system charging notifications + No system channels discovered yet. They will appear here once detected. UI Blur Toggle system-wide UI blur Bubbles From 550f92de778f09208a315a47303e235990f00096 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 10:15:17 +0530 Subject: [PATCH 139/254] Fix search with system notification snoozing changes --- .../domain/registry/FeatureRegistry.kt | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt b/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt index 10a235b2..b0ae9240 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt @@ -181,26 +181,7 @@ object FeatureRegistry { description = R.string.feat_snooze_notifications_desc, permissionKeys = listOf("NOTIFICATION_LISTENER"), showToggle = false, - searchableSettings = listOf( - SearchSetting( - R.string.search_snooze_debug_title, - R.string.search_snooze_debug_desc, - "snooze_debugging", - R.array.keywords_adb_debug - ), - SearchSetting( - R.string.search_snooze_file_title, - R.string.search_snooze_file_desc, - "snooze_file_transfer", - R.array.keywords_mtp - ), - SearchSetting( - R.string.search_snooze_charge_title, - R.string.search_snooze_charge_desc, - "snooze_charging", - R.array.keywords_battery_charge - ) - ) + searchableSettings = emptyList() ) { override fun isEnabled(viewModel: MainViewModel) = false override fun isToggleEnabled(viewModel: MainViewModel, context: Context) = viewModel.isNotificationListenerEnabled.value From cd17d3314edb296d03ec3a15b60e2e620f98af99 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 10:21:35 +0530 Subject: [PATCH 140/254] Fix write secure settings incorrect check --- .../essentials/utils/PermissionUtils.kt | 16 ++-------------- .../viewmodels/StatusBarIconViewModel.kt | 8 +------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/utils/PermissionUtils.kt b/app/src/main/java/com/sameerasw/essentials/utils/PermissionUtils.kt index 83edafc4..e989a93a 100644 --- a/app/src/main/java/com/sameerasw/essentials/utils/PermissionUtils.kt +++ b/app/src/main/java/com/sameerasw/essentials/utils/PermissionUtils.kt @@ -26,20 +26,8 @@ object PermissionUtils { } fun canWriteSecureSettings(context: Context): Boolean { - return try { - val currentValue = Settings.Secure.getString( - context.contentResolver, - "icon_blacklist" - ) - Settings.Secure.putString( - context.contentResolver, - "icon_blacklist", - currentValue ?: "" - ) - true - } catch (e: Exception) { - false - } + return context.checkSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) == + android.content.pm.PackageManager.PERMISSION_GRANTED } fun hasNotificationListenerPermission(context: Context): Boolean { diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/StatusBarIconViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/StatusBarIconViewModel.kt index 5d1efe99..c2e6a01a 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/StatusBarIconViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/StatusBarIconViewModel.kt @@ -360,13 +360,7 @@ class StatusBarIconViewModel : ViewModel() { } private fun canWriteSecureSettings(context: Context): Boolean { - return try { - val currentValue = Settings.Secure.getString(context.contentResolver, ICON_BLACKLIST_SETTING) - Settings.Secure.putString(context.contentResolver, ICON_BLACKLIST_SETTING, currentValue ?: "") - true - } catch (@Suppress("UNUSED_PARAMETER") e: Exception) { - false - } + return com.sameerasw.essentials.utils.PermissionUtils.canWriteSecureSettings(context) } fun getAdbCommand(): String { From 862a1207c828f2998e1c7b9a3914f3066f2c5ef7 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 10:51:20 +0530 Subject: [PATCH 141/254] Fix #26 a workaround for the lack of accessibility overlay on unsupported devices --- .../services/NotificationLightingService.kt | 28 ++++++++++------- .../handlers/NotificationLightingHandler.kt | 30 +++++++++++-------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt b/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt index afb6d484..3ad6859d 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt @@ -229,7 +229,13 @@ class NotificationLightingService : Service() { val color = when { resolvedColor != null -> resolvedColor!! colorMode == NotificationLightingColorMode.CUSTOM -> customColor - else -> getColor(android.R.color.system_accent1_100) + else -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + getColor(android.R.color.system_accent1_100) + } else { + getColor(com.sameerasw.essentials.R.color.purple_500) + } + } } val overlay = OverlayHelper.createOverlayView( @@ -280,21 +286,23 @@ class NotificationLightingService : Service() { private fun getOverlayType(): Int { - // If the accessibility service is enabled, prefer the accessibility overlay type which - // can appear above more system surfaces on some devices (Tasker-style elevation). - return when { - isAccessibilityServiceEnabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> { - // TYPE_ACCESSIBILITY_OVERLAY exists on recent APIs and gives AccessibilityServices - // more privilege to display above other UI in some cases. + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // Android 12+ supports TYPE_ACCESSIBILITY_OVERLAY for AOD visibility + if (isAccessibilityServiceEnabled()) { try { WindowManager.LayoutParams::class.java.getField("TYPE_ACCESSIBILITY_OVERLAY").getInt(null) } catch (_: Exception) { - // Fallback if reflection fails WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } + } else { + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY - else -> WindowManager.LayoutParams.TYPE_PHONE + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Android 8.0-11: Always use TYPE_APPLICATION_OVERLAY for stability + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY + } else { + @Suppress("DEPRECATION") + WindowManager.LayoutParams.TYPE_PHONE } } diff --git a/app/src/main/java/com/sameerasw/essentials/services/handlers/NotificationLightingHandler.kt b/app/src/main/java/com/sameerasw/essentials/services/handlers/NotificationLightingHandler.kt index e093f98e..d50008b8 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/handlers/NotificationLightingHandler.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/handlers/NotificationLightingHandler.kt @@ -73,15 +73,8 @@ class NotificationLightingHandler( } try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { showNotificationLighting() - } else { - // Fallback or ignore for older versions if needed, but logic seems to require S - // Trying to run anyway if compatible, but original code annotated with RequiresApi(S) - // I will wrap calls or assume caller handles version check - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - showNotificationLighting() - } } } catch (e: Exception) { Log.e("NotificationLighting", "Failed to show notification lighting", e) @@ -118,17 +111,30 @@ class NotificationLightingHandler( windowManager = service.getSystemService(Context.WINDOW_SERVICE) as WindowManager val powerManager = service.getSystemService(Context.POWER_SERVICE) as PowerManager - val overlayType = try { - WindowManager.LayoutParams::class.java.getField("TYPE_ACCESSIBILITY_OVERLAY").getInt(null) - } catch (_: Exception) { + val overlayType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + try { + WindowManager.LayoutParams::class.java.getField("TYPE_ACCESSIBILITY_OVERLAY").getInt(null) + } catch (_: Exception) { + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY + } + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY + } else { + @Suppress("DEPRECATION") + WindowManager.LayoutParams.TYPE_PHONE } try { val color = when { resolvedColor != null -> resolvedColor!! colorMode == NotificationLightingColorMode.CUSTOM -> customColor - else -> service.getColor(android.R.color.system_accent1_100) + else -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + service.getColor(android.R.color.system_accent1_100) + } else { + service.getColor(com.sameerasw.essentials.R.color.purple_500) + } + } } val overlay = OverlayHelper.createOverlayView( From 0a738d6c8c45227e69be9453060647e4254665e0 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 11:16:04 +0530 Subject: [PATCH 142/254] Fix edge lighting on Android 11 #26 --- .../services/NotificationLightingService.kt | 20 ++++++--- .../services/NotificationListener.kt | 42 ++++++++++++------- .../handlers/NotificationLightingHandler.kt | 2 +- 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt b/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt index 3ad6859d..a1994ad8 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/NotificationLightingService.kt @@ -11,6 +11,7 @@ import android.content.IntentFilter import android.os.Build import android.os.IBinder import android.provider.Settings +import android.util.Log import android.view.View import android.view.WindowManager import androidx.core.app.NotificationCompat @@ -95,10 +96,18 @@ class NotificationLightingService : Service() { override fun onBind(intent: Intent?): IBinder? = null override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - // All three permissions are required for Notification Lighting to function - if (!canDrawOverlays() || !isAccessibilityServiceEnabled()) { - stopSelf() - return START_NOT_STICKY + Log.d("NotificationLightingSvc", "onStartCommand: action=${intent?.action}") + // Accessibility service Android 12+ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (!canDrawOverlays() || !isAccessibilityServiceEnabled()) { + stopSelf() + return START_NOT_STICKY + } + } else { + if (!canDrawOverlays()) { + stopSelf() + return START_NOT_STICKY + } } // Get corner radius from intent, default to OverlayHelper.CORNER_RADIUS_DP @@ -186,8 +195,7 @@ class NotificationLightingService : Service() { stopSelf() return START_NOT_STICKY } - - stopSelf() + showOverlay() return START_NOT_STICKY } diff --git a/app/src/main/java/com/sameerasw/essentials/services/NotificationListener.kt b/app/src/main/java/com/sameerasw/essentials/services/NotificationListener.kt index e555c542..cb6920b8 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/NotificationListener.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/NotificationListener.kt @@ -66,6 +66,16 @@ class NotificationListener : NotificationListenerService() { @RequiresApi(Build.VERSION_CODES.O) override fun onNotificationPosted(sbn: StatusBarNotification) { + onNotificationPostedInternal(sbn) + } + + @RequiresApi(Build.VERSION_CODES.O) + override fun onNotificationPosted(sbn: StatusBarNotification, rankingMap: RankingMap) { + onNotificationPostedInternal(sbn) + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun onNotificationPostedInternal(sbn: StatusBarNotification) { // Skip our own app's notifications early to avoid flooding logs and redundant processing if (sbn.packageName == packageName) { return @@ -119,7 +129,7 @@ class NotificationListener : NotificationListenerService() { extras.getString(Notification.EXTRA_TEMPLATE) == "android.app.Notification\$MediaStyle" if (isMedia) { - return + return } val prefs = applicationContext.getSharedPreferences("essentials_prefs", Context.MODE_PRIVATE) @@ -129,12 +139,8 @@ class NotificationListener : NotificationListenerService() { if (skipSilent) { val ranking = Ranking() if (currentRanking.getRanking(sbn.key, ranking)) { - val importance = - ranking.importance - - val isSilent = - importance <= android.app.NotificationManager.IMPORTANCE_LOW - + val importance = ranking.importance + val isSilent = importance <= android.app.NotificationManager.IMPORTANCE_LOW if (isSilent) { return } @@ -150,7 +156,8 @@ class NotificationListener : NotificationListenerService() { val enabled = prefs.getBoolean("edge_lighting_enabled", false) if (enabled) { // Check all required permissions before triggering notification lighting - if (hasAllRequiredPermissions()) { + val hasPermissions = hasAllRequiredPermissions() + if (hasPermissions) { // Check if the app is selected for notification lighting val appSelected = isAppSelectedForNotificationLighting(sbn.packageName) if (appSelected) { @@ -262,9 +269,11 @@ class NotificationListener : NotificationListenerService() { return false } - // Check accessibility service is enabled - if (!isAccessibilityServiceEnabled()) { - return false + // Check accessibility service is enabled - only required for Android 12+ AOD support + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (!isAccessibilityServiceEnabled()) { + return false + } } return true @@ -311,14 +320,16 @@ class NotificationListener : NotificationListenerService() { val onlyShowWhenScreenOff = prefs.getBoolean("edge_lighting_only_screen_off", true) if (onlyShowWhenScreenOff) { val powerManager = getSystemService(Context.POWER_SERVICE) as android.os.PowerManager - val isScreenOn = - powerManager.isInteractive + val isScreenOn = powerManager.isInteractive if (isScreenOn) { return false } } - val json = prefs.getString("edge_lighting_selected_apps", null) ?: return true + val json = prefs.getString("edge_lighting_selected_apps", null) + if (json == null) { + return true + } // If no saved preferences, allow all apps by default @@ -328,7 +339,8 @@ class NotificationListener : NotificationListenerService() { // Find the app in the saved list val app = selectedApps.find { it.packageName == packageName } - return app?.isEnabled ?: true // Default to true if app not found + val result = app?.isEnabled ?: true + return result } catch (_: Exception) { // If there's an error, default to allowing all apps (backward compatibility) diff --git a/app/src/main/java/com/sameerasw/essentials/services/handlers/NotificationLightingHandler.kt b/app/src/main/java/com/sameerasw/essentials/services/handlers/NotificationLightingHandler.kt index d50008b8..dc33f7a0 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/handlers/NotificationLightingHandler.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/handlers/NotificationLightingHandler.kt @@ -73,7 +73,7 @@ class NotificationLightingHandler( } try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { showNotificationLighting() } } catch (e: Exception) { From a099ee9a2e6ac49ab8bcdd32e8b41e75d161f6f9 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 11:48:44 +0530 Subject: [PATCH 143/254] Improve #97 System app badges --- .../ui/components/cards/AppToggleItem.kt | 112 ++++++++++++++++-- .../components/sheets/AppSelectionSheets.kt | 2 + .../main/res/drawable/round_android_24.xml | 5 + 3 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 app/src/main/res/drawable/round_android_24.xml diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/cards/AppToggleItem.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/cards/AppToggleItem.kt index 0603b9a9..7ed8bd68 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/cards/AppToggleItem.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/cards/AppToggleItem.kt @@ -16,20 +16,65 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.ui.graphics.Color +import com.sameerasw.essentials.R import com.sameerasw.essentials.utils.HapticUtil +private val GOOGLE_SYSTEM_USER_APPS = setOf( + "com.google.android.apps.scone", + "com.google.android.marvin.talkback", + "com.google.android.projection.gearhead", + "com.google.android.as", + "com.google.android.contactkeys", + "com.google.android.safetycore", + "com.google.android.webview", + "com.google.android.captiveportallogin", + "com.google.ambient.streaming", + "com.google.android.apps.pixel.dcservice", + "com.google.android.apps.turbo", + "com.google.android.apps.work.clouddpc", + "com.google.android.apps.diagnosticstool", + "com.google.android.apps.wellbeing", + "com.google.android.documentsui", + "com.google.android.odad", + "com.google.android.gms", + "com.google.ar.core", + "com.google.vending", + "com.google.android.apps.carrier.carrierwifi", + "com.google.android.modulemetadata", + "com.google.android.networkstack", + "com.google.android.apps.safetyhub", + "com.google.intelligence.sense", + "com.google.android.apps.camera.services", + "com.google.android.apps.nexuslauncher", + "com.google.android.apps.pixel.support", + "com.google.android.as.oss", + "com.android.settings", + "com.google.android.settings.intelligence", + "com.android.stk", + "com.google.android.soundpicker", + "com.google.mainline.telemetry", + "com.google.android.apps.accessibility.voiceaccess", + "com.google.android.cellbroadcastreceiver" +) + @Composable fun AppToggleItem( icon: ImageBitmap?, title: String, modifier: Modifier = Modifier, description: String? = null, + packageName: String? = null, + isSystemApp: Boolean = false, isChecked: Boolean, onCheckedChange: (Boolean) -> Unit, enabled: Boolean = true, @@ -37,6 +82,9 @@ fun AppToggleItem( showToggle: Boolean = true ) { val view = LocalView.current + val shouldShowSystemTag = remember(packageName, isSystemApp) { + isSystemApp || (packageName != null && GOOGLE_SYSTEM_USER_APPS.contains(packageName)) + } Row( modifier = modifier @@ -74,10 +122,33 @@ fun AppToggleItem( if (description != null) { Column(modifier = Modifier.weight(1f)) { - Text( - text = title, - style = MaterialTheme.typography.bodyMedium - ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = title, + style = MaterialTheme.typography.bodyMedium + ) + if (shouldShowSystemTag) { + Box( + modifier = Modifier + .size(18.dp) + .background( + color = MaterialTheme.colorScheme.primary, + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + androidx.compose.material3.Icon( + painter = painterResource(id = R.drawable.round_android_24), + contentDescription = null, + modifier = Modifier.size(12.dp), + tint = MaterialTheme.colorScheme.surfaceBright + ) + } + } + } Text( text = description, style = MaterialTheme.typography.labelMedium, @@ -85,11 +156,34 @@ fun AppToggleItem( ) } } else { - Text( - text = title, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.weight(1f) - ) + Row( + modifier = Modifier.weight(1f), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = title, + style = MaterialTheme.typography.bodyMedium + ) + if (shouldShowSystemTag) { + Box( + modifier = Modifier + .size(18.dp) + .background( + color = MaterialTheme.colorScheme.primary, + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + androidx.compose.material3.Icon( + painter = painterResource(id = R.drawable.round_android_24), + contentDescription = null, + modifier = Modifier.size(12.dp), + tint = MaterialTheme.colorScheme.surfaceBright + ) + } + } + } } if (showToggle) { diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/AppSelectionSheets.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/AppSelectionSheets.kt index a1bf4a11..01080058 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/AppSelectionSheets.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/AppSelectionSheets.kt @@ -221,6 +221,8 @@ fun AppSelectionSheet( AppToggleItem( icon = app.icon, title = app.appName, + packageName = app.packageName, + isSystemApp = app.isSystemApp, isChecked = app.isEnabled, onCheckedChange = { isChecked -> val updatedList = selectedApps.map { diff --git a/app/src/main/res/drawable/round_android_24.xml b/app/src/main/res/drawable/round_android_24.xml new file mode 100644 index 00000000..4db0bf42 --- /dev/null +++ b/app/src/main/res/drawable/round_android_24.xml @@ -0,0 +1,5 @@ + + + + + From f8434e650ff22c12371b306d6b9c2595ce73d90b Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 12:03:04 +0530 Subject: [PATCH 144/254] #26 flashlight pulse support for older devices --- .../services/handlers/FlashlightHandler.kt | 60 +++++++++++++++---- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/services/handlers/FlashlightHandler.kt b/app/src/main/java/com/sameerasw/essentials/services/handlers/FlashlightHandler.kt index c529d4cb..ff2c7d3b 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/handlers/FlashlightHandler.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/handlers/FlashlightHandler.kt @@ -37,7 +37,7 @@ class FlashlightHandler( var isTorchOn = false private set - private var currentTorchId: String? = null + private var primaryCameraId: String? = null private var currentIntensityLevel: Int = 1 private var flashlightJob: Job? = null private var isInternalToggle = false @@ -47,16 +47,18 @@ class FlashlightHandler( private val torchCallback = object : CameraManager.TorchCallback() { override fun onTorchModeChanged(cameraId: String, enabled: Boolean) { + val primaryId = getCameraId() + if (cameraId != primaryId) return // Ignore updates from auxiliary camera IDs + super.onTorchModeChanged(cameraId, enabled) isTorchOn = enabled - currentTorchId = cameraId val prefs = service.getSharedPreferences("essentials_prefs", Context.MODE_PRIVATE) val isGlobalEnabled = prefs.getBoolean("flashlight_global_enabled", false) val lastIntensity = prefs.getInt("flashlight_last_intensity", 1) if (enabled) { - currentTorchId = cameraId + primaryCameraId = cameraId if (isGlobalEnabled && !isInternalToggle) { // External trigger - smoothly fade in to last known intensity @@ -96,7 +98,7 @@ class FlashlightHandler( fun unregister() { torchCallback.let { cameraManager.unregisterTorchCallback(it) } - currentTorchId = null + primaryCameraId = null } fun handleIntent(intent: Intent) { @@ -111,7 +113,7 @@ class FlashlightHandler( if (!isTorchOn) { toggleFlashlight(overrideIntensity = level) } else { - currentTorchId?.let { id -> + getCameraId()?.let { id -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && FlashlightUtil.isIntensitySupported(service, id)) { try { @@ -151,7 +153,7 @@ class FlashlightHandler( notificationManager.createNotificationChannel(channel) } - val cameraId = currentTorchId ?: return + val cameraId = getCameraId() ?: return val maxLevel = FlashlightUtil.getMaxLevel(service, cameraId) val percentage = (intensity * 100) / maxOf(1, maxLevel) @@ -244,12 +246,29 @@ class FlashlightHandler( } private fun getCameraId(): String? { + primaryCameraId?.let { return it } try { + var targetCameraId: String? = null for (id in cameraManager.cameraIdList) { - val characteristics = cameraManager.getCameraCharacteristics(id) - val hasFlash = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false - if (hasFlash) return id + val chars = cameraManager.getCameraCharacteristics(id) + val flashAvailable = chars.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false + val lensFacing = chars.get(CameraCharacteristics.LENS_FACING) + if (flashAvailable && lensFacing == CameraCharacteristics.LENS_FACING_BACK) { + targetCameraId = id + break + } } + if (targetCameraId == null) { + for (id in cameraManager.cameraIdList) { + val chars = cameraManager.getCameraCharacteristics(id) + if (chars.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true) { + targetCameraId = id + break + } + } + } + primaryCameraId = targetCameraId + return targetCameraId } catch (e: Exception) { Log.e("Flashlight", "Error getting camera ID", e) } @@ -285,7 +304,7 @@ class FlashlightHandler( val faceDownOnly = prefs.getBoolean("flashlight_pulse_facedown_only", true) if (faceDownOnly && !isProximityBlocked) return - val cameraId = currentTorchId ?: getCameraId() ?: return + val cameraId = getCameraId() ?: return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && FlashlightUtil.isIntensitySupported(service, cameraId)) { @@ -319,6 +338,23 @@ class FlashlightHandler( isInternalToggle = false } + } else { + // Fallback for older versions or devices without intensity support + Log.d("Flashlight", "Pulse fallback with cameraId: $cameraId") + flashlightJob?.cancel() + flashlightJob = scope.launch { + isInternalToggle = true + try { + cameraManager.setTorchMode(cameraId, true) + kotlinx.coroutines.delay(700L) + cameraManager.setTorchMode(cameraId, false) + kotlinx.coroutines.delay(200L) + } catch (e: Exception) { + Log.e("Flashlight", "Fallback pulse failed for cameraId: $cameraId", e) + } finally { + isInternalToggle = false + } + } } } @@ -328,7 +364,7 @@ class FlashlightHandler( } fun adjustFlashlightIntensity(increase: Boolean) { - val cameraId = currentTorchId ?: return + val cameraId = getCameraId() ?: return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return try { @@ -406,7 +442,7 @@ class FlashlightHandler( if (targetCameraId != null) { val finalCameraId = targetCameraId - currentTorchId = finalCameraId + primaryCameraId = finalCameraId val maxLevel = FlashlightUtil.getMaxLevel(service, finalCameraId) val defaultLevel = FlashlightUtil.getDefaultLevel(service, finalCameraId) From e8cd2d86e6a642a30178b2c7defd974ff3cb3d01 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 12:43:34 +0530 Subject: [PATCH 145/254] #75 Private DNS QS tile --- app/src/main/AndroidManifest.xml | 11 +++ .../domain/registry/FeatureRegistry.kt | 7 ++ .../services/tiles/PrivateDnsTileService.kt | 84 +++++++++++++++++++ .../configs/QuickSettingsTilesSettingsUI.kt | 4 +- app/src/main/res/drawable/rounded_dns_24.xml | 5 ++ app/src/main/res/values/strings.xml | 6 ++ 6 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/sameerasw/essentials/services/tiles/PrivateDnsTileService.kt create mode 100644 app/src/main/res/drawable/rounded_dns_24.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c2359cc1..f5bf4d72 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -521,6 +521,17 @@ + + + + + + diff --git a/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt b/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt index b0ae9240..414b8cc6 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt @@ -300,6 +300,13 @@ object FeatureRegistry { "Stay awake", R.array.keywords_qs_stay_awake, R.string.feat_qs_tiles_title + ), + SearchSetting( + R.string.search_qs_private_dns_title, + R.string.search_qs_private_dns_desc, + "Private DNS", + R.array.keywords_network_visibility, + R.string.feat_qs_tiles_title ) ) ) { diff --git a/app/src/main/java/com/sameerasw/essentials/services/tiles/PrivateDnsTileService.kt b/app/src/main/java/com/sameerasw/essentials/services/tiles/PrivateDnsTileService.kt new file mode 100644 index 00000000..221a8b22 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/services/tiles/PrivateDnsTileService.kt @@ -0,0 +1,84 @@ +package com.sameerasw.essentials.services.tiles + +import android.content.Intent +import android.graphics.drawable.Icon +import android.os.Build +import android.provider.Settings +import android.service.quicksettings.Tile +import androidx.annotation.RequiresApi +import com.sameerasw.essentials.FeatureSettingsActivity +import com.sameerasw.essentials.R +import com.sameerasw.essentials.utils.PermissionUtils + +@RequiresApi(Build.VERSION_CODES.N) +class PrivateDnsTileService : BaseTileService() { + + companion object { + private const val PRIVATE_DNS_MODE = "private_dns_mode" + private const val PRIVATE_DNS_SPECIFIER = "private_dns_specifier" + + private const val MODE_OFF = "off" + private const val MODE_AUTO = "opportunistic" + private const val MODE_HOSTNAME = "hostname" + } + + override fun onClick() { + if (!hasFeaturePermission()) { + val intent = Intent(this, FeatureSettingsActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + putExtra("feature", "Quick settings tiles") + } + startActivityAndCollapse(intent) + return + } + super.onClick() + } + + override fun getTileLabel(): String = getString(R.string.tile_private_dns) + + override fun getTileSubtitle(): String { + return when (getPrivateDnsMode()) { + MODE_AUTO -> getString(R.string.tile_private_dns_auto) + MODE_HOSTNAME -> getPrivateDnsHostname() ?: getString(R.string.feat_qs_tiles_title) + else -> getString(R.string.tile_private_dns_off) + } + } + + override fun hasFeaturePermission(): Boolean { + return PermissionUtils.canWriteSecureSettings(this) + } + + override fun getTileIcon(): Icon? { + return Icon.createWithResource(this, R.drawable.rounded_dns_24) + } + + override fun getTileState(): Int { + return if (getPrivateDnsMode() != MODE_OFF) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE + } + + override fun onTileClick() { + val currentMode = getPrivateDnsMode() + val nextMode = when (currentMode) { + MODE_OFF -> MODE_AUTO + MODE_AUTO -> { + if (getPrivateDnsHostname().isNullOrEmpty()) MODE_OFF else MODE_HOSTNAME + } + MODE_HOSTNAME -> MODE_OFF + else -> MODE_OFF + } + + try { + Settings.Global.putString(contentResolver, PRIVATE_DNS_MODE, nextMode) + } catch (e: Exception) { + // Handle error or permission missing + } + } + + private fun getPrivateDnsMode(): String { + return Settings.Global.getString(contentResolver, PRIVATE_DNS_MODE) ?: MODE_OFF + } + + private fun getPrivateDnsHostname(): String? { + return Settings.Global.getString(contentResolver, PRIVATE_DNS_SPECIFIER) + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/QuickSettingsTilesSettingsUI.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/QuickSettingsTilesSettingsUI.kt index 5aba27f3..843a641b 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/QuickSettingsTilesSettingsUI.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/QuickSettingsTilesSettingsUI.kt @@ -45,6 +45,7 @@ import com.sameerasw.essentials.services.tiles.NfcTileService import com.sameerasw.essentials.services.tiles.StayAwakeTileService import com.sameerasw.essentials.services.tiles.AdaptiveBrightnessTileService import com.sameerasw.essentials.services.tiles.MapsPowerSavingTileService +import com.sameerasw.essentials.services.tiles.PrivateDnsTileService import com.sameerasw.essentials.ui.modifiers.highlight data class QSTileInfo( @@ -80,7 +81,8 @@ fun QuickSettingsTilesSettingsUI( QSTileInfo(R.string.tile_stay_awake, R.drawable.rounded_av_timer_24, StayAwakeTileService::class.java), QSTileInfo(R.string.nfc_tile_label, R.drawable.rounded_nfc_24, NfcTileService::class.java), QSTileInfo(R.string.tile_adaptive_brightness, R.drawable.rounded_brightness_auto_24, AdaptiveBrightnessTileService::class.java), - QSTileInfo(R.string.feat_maps_power_saving_title, R.drawable.rounded_navigation_24, MapsPowerSavingTileService::class.java) + QSTileInfo(R.string.feat_maps_power_saving_title, R.drawable.rounded_navigation_24, MapsPowerSavingTileService::class.java), + QSTileInfo(R.string.tile_private_dns, R.drawable.rounded_dns_24, PrivateDnsTileService::class.java) ) Column( diff --git a/app/src/main/res/drawable/rounded_dns_24.xml b/app/src/main/res/drawable/rounded_dns_24.xml new file mode 100644 index 00000000..1cce0c46 --- /dev/null +++ b/app/src/main/res/drawable/rounded_dns_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 947f7733..101fdf51 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -179,6 +179,10 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off On Off @@ -370,6 +374,8 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 409aaecf4c1cbe4e4d548440ccfbd129b4f36a56 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 12:48:40 +0530 Subject: [PATCH 146/254] #75 USB debugging QS tile --- app/src/main/AndroidManifest.xml | 11 ++++ .../domain/registry/FeatureRegistry.kt | 7 +++ .../services/tiles/UsbDebuggingTileService.kt | 62 +++++++++++++++++++ .../configs/QuickSettingsTilesSettingsUI.kt | 4 +- app/src/main/res/values/strings.xml | 4 ++ 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/sameerasw/essentials/services/tiles/UsbDebuggingTileService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f5bf4d72..2868f341 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -532,6 +532,17 @@ + + + + + + diff --git a/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt b/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt index 414b8cc6..256c831b 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/registry/FeatureRegistry.kt @@ -307,6 +307,13 @@ object FeatureRegistry { "Private DNS", R.array.keywords_network_visibility, R.string.feat_qs_tiles_title + ), + SearchSetting( + R.string.search_qs_usb_debugging_title, + R.string.search_qs_usb_debugging_desc, + "USB Debugging", + R.array.keywords_adb_debug, + R.string.feat_qs_tiles_title ) ) ) { diff --git a/app/src/main/java/com/sameerasw/essentials/services/tiles/UsbDebuggingTileService.kt b/app/src/main/java/com/sameerasw/essentials/services/tiles/UsbDebuggingTileService.kt new file mode 100644 index 00000000..01e9bfd0 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/services/tiles/UsbDebuggingTileService.kt @@ -0,0 +1,62 @@ +package com.sameerasw.essentials.services.tiles + +import android.content.Intent +import android.graphics.drawable.Icon +import android.os.Build +import android.provider.Settings +import android.service.quicksettings.Tile +import androidx.annotation.RequiresApi +import com.sameerasw.essentials.FeatureSettingsActivity +import com.sameerasw.essentials.R +import com.sameerasw.essentials.utils.PermissionUtils + +@RequiresApi(Build.VERSION_CODES.N) +class UsbDebuggingTileService : BaseTileService() { + + override fun onClick() { + if (!hasFeaturePermission()) { + val intent = Intent(this, FeatureSettingsActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + putExtra("feature", "Quick settings tiles") + } + startActivityAndCollapse(intent) + return + } + super.onClick() + } + + override fun getTileLabel(): String = getString(R.string.tile_usb_debugging) + + override fun getTileSubtitle(): String { + return if (isAdbEnabled()) getString(R.string.on) else getString(R.string.off) + } + + override fun hasFeaturePermission(): Boolean { + return PermissionUtils.canWriteSecureSettings(this) + } + + override fun getTileIcon(): Icon? { + return Icon.createWithResource(this, R.drawable.rounded_adb_24) + } + + override fun getTileState(): Int { + return if (isAdbEnabled()) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE + } + + override fun onTileClick() { + val newState = if (isAdbEnabled()) 0 else 1 + try { + Settings.Global.putInt(contentResolver, Settings.Global.ADB_ENABLED, newState) + } catch (e: Exception) { + // Permission check in BaseTileService should handle this + } + } + + private fun isAdbEnabled(): Boolean { + return try { + Settings.Global.getInt(contentResolver, Settings.Global.ADB_ENABLED) == 1 + } catch (e: Exception) { + false + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/QuickSettingsTilesSettingsUI.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/QuickSettingsTilesSettingsUI.kt index 843a641b..bf3a6316 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/QuickSettingsTilesSettingsUI.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/configs/QuickSettingsTilesSettingsUI.kt @@ -46,6 +46,7 @@ import com.sameerasw.essentials.services.tiles.StayAwakeTileService import com.sameerasw.essentials.services.tiles.AdaptiveBrightnessTileService import com.sameerasw.essentials.services.tiles.MapsPowerSavingTileService import com.sameerasw.essentials.services.tiles.PrivateDnsTileService +import com.sameerasw.essentials.services.tiles.UsbDebuggingTileService import com.sameerasw.essentials.ui.modifiers.highlight data class QSTileInfo( @@ -82,7 +83,8 @@ fun QuickSettingsTilesSettingsUI( QSTileInfo(R.string.nfc_tile_label, R.drawable.rounded_nfc_24, NfcTileService::class.java), QSTileInfo(R.string.tile_adaptive_brightness, R.drawable.rounded_brightness_auto_24, AdaptiveBrightnessTileService::class.java), QSTileInfo(R.string.feat_maps_power_saving_title, R.drawable.rounded_navigation_24, MapsPowerSavingTileService::class.java), - QSTileInfo(R.string.tile_private_dns, R.drawable.rounded_dns_24, PrivateDnsTileService::class.java) + QSTileInfo(R.string.tile_private_dns, R.drawable.rounded_dns_24, PrivateDnsTileService::class.java), + QSTileInfo(R.string.tile_usb_debugging, R.drawable.rounded_adb_24, UsbDebuggingTileService::class.java) ) Column( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 101fdf51..d811709c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -183,6 +183,8 @@ Toggle Private DNS mode Auto Off + USB Debugging + Toggle USB Debugging On Off @@ -376,6 +378,8 @@ Toggle stay awake developer option Private DNS Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From a1fb4d3adc1bde696c9074373d95977b52157b2e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:37 +0530 Subject: [PATCH 147/254] New translations strings.xml (Romanian) --- app/src/main/res/values-ro/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 7096b11d..ccee6686 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging Pornit Oprit @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 85957e4125103e2b7c98368116f11d25bab16d00 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:38 +0530 Subject: [PATCH 148/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index cbf85337..110f6085 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -173,6 +173,12 @@ Actif Inactif NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging Activer Désactiver @@ -352,6 +358,10 @@ Impulsion de lampe torche Activer/désactiver l\'impulsion de lampe-torche Basculer l\'option développeur \"Rester allumé\" + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Activer la réattribution de boutons Basculer globalement la réattribution du bouton volume Reconfigurer le retour haptique From 9c19963bee3859f1c8fa836b83e279cabbc91f17 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:39 +0530 Subject: [PATCH 149/254] New translations strings.xml (Spanish) --- app/src/main/res/values-es/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 27882fa08ff92ada530d57982608e4ffd71cb2ee Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:41 +0530 Subject: [PATCH 150/254] New translations strings.xml (Afrikaans) --- app/src/main/res/values-af/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 45f49c86274aed7bad685bb066092e1cb62da194 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:42 +0530 Subject: [PATCH 151/254] New translations strings.xml (Arabic) --- app/src/main/res/values-ar/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index f5cfaa71..bd831804 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 01f8e52b1d54773c7c206984851a342a4f19ff36 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:43 +0530 Subject: [PATCH 152/254] New translations strings.xml (Catalan) --- app/src/main/res/values-ca/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 09511b4f4f04d5350499fe7ff303448d7183a60d Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:44 +0530 Subject: [PATCH 153/254] New translations strings.xml (Czech) --- app/src/main/res/values-cs/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From d54170456fdce3a85e9c91b5a22dbee26d2fb48c Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:45 +0530 Subject: [PATCH 154/254] New translations strings.xml (Danish) --- app/src/main/res/values-da/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 3ef19ef3ab1c7063a82518c3468c45ced8089221 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:46 +0530 Subject: [PATCH 155/254] New translations strings.xml (German) --- app/src/main/res/values-de/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f0ad691b..e80b6714 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -173,6 +173,12 @@ Aktiv Inaktiv NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging An Aus @@ -352,6 +358,10 @@ Taschenlampenimpuls Taschenlampenimpuls bei Benachrichtigungen ein-/ausschalten Entwickleroption „Wach bleiben“ umschalten + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Aktiviere Tastenneubelegung Hauptschalter für die Neuzuweisung der Lautstärketasten Haptisches Feedback neu zuordnen From 818e9a1f4d705b958772d60a295efa0169159a15 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:47 +0530 Subject: [PATCH 156/254] New translations strings.xml (Greek) --- app/src/main/res/values-el/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From db46c9ff3247749a0ffdd2b05e8c2a8b9358c3ff Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:49 +0530 Subject: [PATCH 157/254] New translations strings.xml (Finnish) --- app/src/main/res/values-fi/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 942de6b8d9dc20f255a36f18bf227554478d21ee Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:50 +0530 Subject: [PATCH 158/254] New translations strings.xml (Hebrew) --- app/src/main/res/values-he/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From acd2d9c1f7ced16a00b1ebda86f9e9ddae77b762 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:51 +0530 Subject: [PATCH 159/254] New translations strings.xml (Hungarian) --- app/src/main/res/values-hu/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 73e47f786893be5c2ef0d87043dd62ea3dc6fa14 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:52 +0530 Subject: [PATCH 160/254] New translations strings.xml (Italian) --- app/src/main/res/values-it/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 96fa8a89..366b4ab8 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -174,6 +174,12 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -353,6 +359,10 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 59fe11b5a0da43750bc68166d2a801a48ae39ec1 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:54 +0530 Subject: [PATCH 161/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index e48b4487..31a2fa69 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -173,6 +173,12 @@ 動作中 停止中 NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging オン オフ @@ -352,6 +358,10 @@ ライト点滅 通知が来たときにライトを点滅させるか切り替える 開発者向けオプションのスリープモードにしないを切り替える + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option ボタンリマップを有効にする 音量ボタンの機能を再設定する機能を使うか切り替える リマップ触覚フィードバック From 90ee808e140ea0add82e0a1e67f4a25f7bb26379 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:55 +0530 Subject: [PATCH 162/254] New translations strings.xml (Korean) --- app/src/main/res/values-ko/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 83d4d74944f28b026fe2068e588c82351d34ec28 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:56 +0530 Subject: [PATCH 163/254] New translations strings.xml (Dutch) --- app/src/main/res/values-nl/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 04abc4467aa7b4c975cdcf1cd45a18b51c309336 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:57 +0530 Subject: [PATCH 164/254] New translations strings.xml (Norwegian) --- app/src/main/res/values-no/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From c499757487e05989e95902f7d8d84ef0eab3deb7 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:47:58 +0530 Subject: [PATCH 165/254] New translations strings.xml (Polish) --- app/src/main/res/values-pl/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From dbc021007faebb07eef797abb1fd37ea6d4f8948 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:00 +0530 Subject: [PATCH 166/254] New translations strings.xml (Portuguese) --- app/src/main/res/values-pt/strings.xml | 28 +++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index fe0f5b41..e3e217e2 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback @@ -376,7 +386,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -759,7 +769,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -778,14 +788,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -810,8 +820,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining + Travel alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From b72b4530dc7658e202b839c48410a65a58a57cc4 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:01 +0530 Subject: [PATCH 167/254] New translations strings.xml (Russian) --- app/src/main/res/values-ru/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index dcc60f68..3efdd643 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 17d363b2404b0cd460c6c16a0b776569fd09e450 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:02 +0530 Subject: [PATCH 168/254] New translations strings.xml (Serbian (Cyrillic)) --- app/src/main/res/values-sr/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From abfc14e955354fdca78cf1b0f9dfeba20b274888 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:03 +0530 Subject: [PATCH 169/254] New translations strings.xml (Swedish) --- app/src/main/res/values-sv/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 6ac07b13559678bd533c0a271bc78d5cbef1bcf9 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:04 +0530 Subject: [PATCH 170/254] New translations strings.xml (Turkish) --- app/src/main/res/values-tr/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 978c05393926f20aff7672770c388837132a92ca Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:05 +0530 Subject: [PATCH 171/254] New translations strings.xml (Ukrainian) --- app/src/main/res/values-uk/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 1bc345e9170ec7a6cd411c9dba5d324f5ea701a4 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:07 +0530 Subject: [PATCH 172/254] New translations strings.xml (Chinese Simplified) --- app/src/main/res/values-zh/strings.xml | 28 +++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index fe0f5b41..e3e217e2 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback @@ -376,7 +386,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -759,7 +769,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -778,14 +788,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -810,8 +820,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining + Travel alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From 43d2b65e99d9c979e62ff7e075e739e24264c75f Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:08 +0530 Subject: [PATCH 173/254] New translations strings.xml (Chinese Traditional) --- app/src/main/res/values-zh/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index e3e217e2..8987af25 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -386,7 +386,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -769,7 +769,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -788,14 +788,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -820,8 +820,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From 70d2e2639f3861b1693028ec8c69d1053935bf67 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:09 +0530 Subject: [PATCH 174/254] New translations strings.xml (English) --- app/src/main/res/values-en/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 4123b0325d927238b98d95553533fd670cc50673 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:10 +0530 Subject: [PATCH 175/254] New translations strings.xml (Vietnamese) --- app/src/main/res/values-vi/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 12ab1c8d231f6d6a8936befd59dc2f4bf3adb578 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:11 +0530 Subject: [PATCH 176/254] New translations strings.xml (Portuguese, Brazilian) --- app/src/main/res/values-pt/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index e3e217e2..8987af25 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -386,7 +386,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -769,7 +769,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -788,14 +788,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -820,8 +820,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From d85914bdba38b4a195621541fae395f9f9ee92df Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:13 +0530 Subject: [PATCH 177/254] New translations strings.xml (Sinhala) --- app/src/main/res/values-si/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 67008120e2347d6e3e656a91e5c4fd74db84fbee Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 13:48:14 +0530 Subject: [PATCH 178/254] New translations strings.xml (Acholi) --- app/src/main/res/values-ach/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-ach/strings.xml b/app/src/main/res/values-ach/strings.xml index 4b1e94b0..f7e2fff0 100644 --- a/app/src/main/res/values-ach/strings.xml +++ b/app/src/main/res/values-ach/strings.xml @@ -173,6 +173,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -352,6 +358,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From 31352a46b1545e2ba3c218a330e26c517e0719a8 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 15:13:22 +0530 Subject: [PATCH 179/254] initial watermark implementation --- app/build.gradle.kts | 5 +- app/src/main/AndroidManifest.xml | 12 + .../domain/registry/FeatureRegistry.kt | 15 + .../domain/watermark/MetadataProvider.kt | 45 ++ .../domain/watermark/WatermarkEngine.kt | 232 +++++++++++ .../domain/watermark/WatermarkRepository.kt | 54 +++ .../ui/components/pickers/SegmentedPicker.kt | 12 +- .../watermark/WatermarkActivity.kt | 70 ++++ .../composables/watermark/WatermarkPreview.kt | 78 ++++ .../composables/watermark/WatermarkScreen.kt | 393 ++++++++++++++++++ .../viewmodels/WatermarkViewModel.kt | 272 ++++++++++++ .../rounded_add_photo_alternate_24.xml | 5 + .../res/drawable/rounded_window_open_24.xml | 5 + app/src/main/res/values/strings.xml | 12 + 14 files changed, 1208 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/sameerasw/essentials/domain/watermark/MetadataProvider.kt create mode 100644 app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt create mode 100644 app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt create mode 100644 app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt create mode 100644 app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkPreview.kt create mode 100644 app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt create mode 100644 app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt create mode 100644 app/src/main/res/drawable/rounded_add_photo_alternate_24.xml create mode 100644 app/src/main/res/drawable/rounded_window_open_24.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0191243f..d3d57b96 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -105,7 +105,10 @@ dependencies { // SymSpell for word suggestions implementation("com.darkrockstudios:symspellkt:3.4.0") - // Glance for Widgets implementation("androidx.glance:glance-appwidget:1.1.0") implementation("androidx.glance:glance-material3:1.1.0") + + // Watermark dependencies + implementation("androidx.exifinterface:exifinterface:1.3.7") + implementation("androidx.compose.material:material-icons-extended:1.7.0") // Compatible with Compose BOM } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2868f341..9c613a0a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -205,6 +205,18 @@ android:taskAffinity="" android:theme="@style/Theme.Essentials.Translucent" /> + + + + + + + + drawOverlay(bitmap, exifData, options) + WatermarkStyle.FRAME -> drawFrame(bitmap, exifData, options) + } + } + + private fun drawOverlay(bitmap: Bitmap, exifData: ExifData, options: WatermarkOptions): Bitmap { + val canvas = Canvas(bitmap) + val useDark = options.useDarkTheme + val textColor = if (useDark) Color.BLACK else Color.WHITE + val shadowColor = if (useDark) Color.WHITE else Color.BLACK + + val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = textColor + textSize = bitmap.width * 0.03f // 3% of width + setShadowLayer(4f, 2f, 2f, shadowColor) + } + + val margin = bitmap.width * 0.05f + var yPos = bitmap.height - margin + + if (options.showExif) { + val exifString = buildExifString(exifData) + if (exifString.isNotEmpty()) { + val textBounds = Rect() + paint.getTextBounds(exifString, 0, exifString.length, textBounds) + canvas.drawText(exifString, bitmap.width - margin - textBounds.width(), yPos, paint) + yPos -= textBounds.height() * 1.5f + } + } + + if (options.showDeviceBrand) { + val brandString = buildBrandString(exifData) + val brandPaint = Paint(paint).apply { + typeface = Typeface.DEFAULT_BOLD + } + val textBounds = Rect() + brandPaint.getTextBounds(brandString, 0, brandString.length, textBounds) + canvas.drawText(brandString, bitmap.width - margin - textBounds.width(), yPos, brandPaint) + } + + return bitmap + } + + private fun drawFrame(bitmap: Bitmap, exifData: ExifData, options: WatermarkOptions): Bitmap { + val frameHeight = (bitmap.height * 0.10f).roundToInt() // 10% chin + val newHeight = bitmap.height + frameHeight + + val finalBitmap = Bitmap.createBitmap(bitmap.width, newHeight, Bitmap.Config.ARGB_8888) + val canvas = Canvas(finalBitmap) + + val useDark = options.useDarkTheme + val bgColor = if (useDark) Color.BLACK else Color.WHITE + val textColor = if (useDark) Color.WHITE else Color.BLACK + val secondaryTextColor = if (useDark) Color.LTGRAY else Color.GRAY + + // Draw background + canvas.drawColor(bgColor) + + // Draw original image + canvas.drawBitmap(bitmap, 0f, 0f, null) + + // Draw Text in Chin + val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = textColor + textSize = frameHeight * 0.25f + } + + val leftMargin = bitmap.width * 0.05f + val centerY = bitmap.height + (frameHeight / 2f) + (paint.textSize / 3f) + + // Left side: Brand / Model + if (options.showDeviceBrand) { + val brandPaint = Paint(paint).apply { + typeface = Typeface.DEFAULT_BOLD + textSize = frameHeight * 0.3f + } + val brandString = buildBrandString(exifData) + canvas.drawText(brandString, leftMargin, centerY, brandPaint) + } + + // Right side: EXIF + if (options.showExif) { + val exifString = buildExifString(exifData) + val exifPaint = Paint(paint).apply { + color = secondaryTextColor + textSize = frameHeight * 0.2f + } + val textBounds = Rect() + exifPaint.getTextBounds(exifString, 0, exifString.length, textBounds) + canvas.drawText(exifString, bitmap.width - leftMargin - textBounds.width(), centerY, exifPaint) + } + + return finalBitmap + } + + private fun buildBrandString(exif: ExifData): String { + return if (!exif.make.isNullOrEmpty() && !exif.model.isNullOrEmpty()) { + if (exif.model.contains(exif.make, ignoreCase = true)) { + exif.model + } else { + "${exif.make} ${exif.model}" + } + } else { + exif.model ?: exif.make ?: "Shot on Device" + } + } + + private fun buildExifString(exif: ExifData): String { + val parts = mutableListOf() + exif.focalLength?.let { parts.add(it) } + exif.aperture?.let { parts.add(it) } + exif.shutterSpeed?.let { parts.add(it) } + exif.iso?.let { parts.add(it) } + return parts.joinToString(" • ") + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt new file mode 100644 index 00000000..f50f2960 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt @@ -0,0 +1,54 @@ +package com.sameerasw.essentials.domain.watermark + +import android.content.Context +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +// Setup DataStore extension +private val Context.dataStore by preferencesDataStore(name = "watermark_prefs") + +class WatermarkRepository( + private val context: Context +) { + private val PREF_STYLE = stringPreferencesKey("watermark_style") + private val PREF_SHOW_BRAND = booleanPreferencesKey("show_brand") + private val PREF_SHOW_EXIF = booleanPreferencesKey("show_exif") + private val PREF_USE_DARK_THEME = booleanPreferencesKey("use_dark_theme") + + val watermarkOptions: Flow = context.dataStore.data + .map { preferences -> + val styleStr = preferences[PREF_STYLE] ?: WatermarkStyle.FRAME.name + val style = try { + WatermarkStyle.valueOf(styleStr) + } catch (e: Exception) { + WatermarkStyle.FRAME + } + + WatermarkOptions( + style = style, + showDeviceBrand = preferences[PREF_SHOW_BRAND] ?: true, + showExif = preferences[PREF_SHOW_EXIF] ?: true, + useDarkTheme = preferences[PREF_USE_DARK_THEME] ?: false + ) + } + + suspend fun updateStyle(style: WatermarkStyle) { + context.dataStore.edit { it[PREF_STYLE] = style.name } + } + + suspend fun updateShowBrand(show: Boolean) { + context.dataStore.edit { it[PREF_SHOW_BRAND] = show } + } + + suspend fun updateShowExif(show: Boolean) { + context.dataStore.edit { it[PREF_SHOW_EXIF] = show } + } + + suspend fun updateUseDarkTheme(useDark: Boolean) { + context.dataStore.edit { it[PREF_USE_DARK_THEME] = useDark } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/pickers/SegmentedPicker.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/pickers/SegmentedPicker.kt index 7e4c3a4a..8a945f9a 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/pickers/SegmentedPicker.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/pickers/SegmentedPicker.kt @@ -26,6 +26,7 @@ fun SegmentedPicker( selectedItem: T, onItemSelected: (T) -> Unit, labelProvider: (T) -> String, + iconProvider: (@Composable (T) -> Unit)? = null, modifier: Modifier = Modifier, cornerShape: CornerSize = MaterialTheme.shapes.extraSmall.bottomEnd, ) { @@ -55,7 +56,16 @@ fun SegmentedPicker( else -> ButtonGroupDefaults.connectedMiddleButtonShapes() }, ) { - Text(labelProvider(item)) + Row( + horizontalArrangement = Arrangement.Center, + verticalAlignment = androidx.compose.ui.Alignment.CenterVertically + ) { + if (iconProvider != null) { + iconProvider(item) + androidx.compose.foundation.layout.Spacer(Modifier.padding(end = 8.dp)) + } + Text(labelProvider(item)) + } } } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt new file mode 100644 index 00000000..d1487121 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt @@ -0,0 +1,70 @@ +package com.sameerasw.essentials.ui.composables.watermark + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.viewmodel.compose.viewModel +import com.sameerasw.essentials.R +import com.sameerasw.essentials.ui.theme.EssentialsTheme +import com.sameerasw.essentials.viewmodels.WatermarkViewModel + +class WatermarkActivity : ComponentActivity() { + + private var initialUri by mutableStateOf(null) + + private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> + if (uri != null) { + try { + val flag = Intent.FLAG_GRANT_READ_URI_PERMISSION + contentResolver.takePersistableUriPermission(uri, flag) + } catch (e: Exception) { + // Ignore if not persistable + } + initialUri = uri + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + + // Handle Share Intent + if (intent?.action == Intent.ACTION_SEND && intent.type?.startsWith("image/") == true) { + (intent.getParcelableExtra(Intent.EXTRA_STREAM))?.let { + initialUri = it + } + } + + setContent { + EssentialsTheme { + Surface(color = MaterialTheme.colorScheme.background) { + val context = LocalContext.current + val viewModel: WatermarkViewModel = viewModel( + factory = WatermarkViewModel.provideFactory(context) + ) + + WatermarkScreen( + initialUri = initialUri, + onPickImage = { + pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + }, + onBack = { finish() }, + viewModel = viewModel + ) + } + } + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkPreview.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkPreview.kt new file mode 100644 index 00000000..c3650a6a --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkPreview.kt @@ -0,0 +1,78 @@ +package com.sameerasw.essentials.ui.composables.watermark + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.sameerasw.essentials.R +import com.sameerasw.essentials.viewmodels.WatermarkUiState +import java.io.File + +@Composable +fun WatermarkPreview( + uiState: WatermarkUiState, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + when (uiState) { + is WatermarkUiState.Idle -> { + Text( + text = stringResource(R.string.watermark_pick_image), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + is WatermarkUiState.Processing -> { + CircularProgressIndicator() + } + is WatermarkUiState.Success -> { + val targetFile = uiState.file + var visibleFile by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(targetFile) } + var targetReady by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(false) } + + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + AsyncImage( + model = visibleFile, + contentDescription = null, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Fit + ) + + if (targetFile != visibleFile) { + AsyncImage( + model = targetFile, + contentDescription = "Preview", + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Fit, + onSuccess = { + visibleFile = targetFile + } + ) + } + } + } + is WatermarkUiState.Error -> { + Text( + text = uiState.message, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.error + ) + } + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt new file mode 100644 index 00000000..a8e28a76 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt @@ -0,0 +1,393 @@ +package com.sameerasw.essentials.ui.composables.watermark + +import android.graphics.drawable.Icon +import android.net.Uri +import android.widget.Toast +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LoadingIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SegmentedButtonDefaults.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.blur +import androidx.compose.ui.draw.clip +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.sameerasw.essentials.R +import com.sameerasw.essentials.domain.watermark.WatermarkStyle +import com.sameerasw.essentials.ui.components.ReusableTopAppBar +import com.sameerasw.essentials.ui.components.cards.IconToggleItem +import com.sameerasw.essentials.ui.components.containers.RoundedCardContainer +import com.sameerasw.essentials.ui.components.pickers.SegmentedPicker +import com.sameerasw.essentials.viewmodels.WatermarkUiState +import com.sameerasw.essentials.viewmodels.WatermarkViewModel + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun WatermarkScreen( + initialUri: Uri?, + onPickImage: () -> Unit, + onBack: () -> Unit, + viewModel: WatermarkViewModel +) { + val context = LocalContext.current + val options by viewModel.options.collectAsState() + val previewState by viewModel.previewUiState.collectAsState() + val saveState by viewModel.uiState.collectAsState() + + LaunchedEffect(initialUri) { + if (initialUri != null) { + viewModel.loadPreview(initialUri) + } + } + + LaunchedEffect(saveState) { + when (saveState) { + is WatermarkUiState.Success -> { + Toast.makeText(context, R.string.watermark_save_success, Toast.LENGTH_SHORT).show() + viewModel.resetState() + } + is WatermarkUiState.Error -> { + Toast.makeText(context, (saveState as WatermarkUiState.Error).message, Toast.LENGTH_SHORT).show() + viewModel.resetState() + } + else -> {} + } + } + + Scaffold( + topBar = { + ReusableTopAppBar( + title = R.string.feat_watermark_title, + hasBack = true, + onBackClick = onBack, + isSmall = true, + actions = { + val pickImageButton = @Composable { + // Pick Image Button + if (initialUri == null) { + // Primary when no image + androidx.compose.material3.Button(onClick = onPickImage) { + Icon( + painter = androidx.compose.ui.res.painterResource(R.drawable.rounded_add_photo_alternate_24), + contentDescription = stringResource(R.string.watermark_pick_image), + modifier = Modifier.size(18.dp) + ) + Spacer(Modifier.size(8.dp)) + Text(stringResource(R.string.watermark_pick_image)) + } + } else { + // Secondary when image is there + androidx.compose.material3.OutlinedButton(onClick = onPickImage) { + Icon( + painter = androidx.compose.ui.res.painterResource(R.drawable.rounded_add_photo_alternate_24), + contentDescription = stringResource(R.string.watermark_pick_image), + modifier = Modifier.size(18.dp) + ) + } + } + } + + pickImageButton() + + // Save/Share Menu Button + if (initialUri != null) { + Spacer(Modifier.size(8.dp)) + var showMenu by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(false) } + + Box { + // Save Button (Primary) + androidx.compose.material3.Button(onClick = { showMenu = true }) { + Icon( + painter = androidx.compose.ui.res.painterResource(R.drawable.rounded_save_24), + contentDescription = stringResource(R.string.action_save), + modifier = Modifier.size(18.dp) + ) + Spacer(Modifier.size(8.dp)) + Text(stringResource(R.string.action_save)) + } + + com.sameerasw.essentials.ui.components.menus.SegmentedDropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false }, + ) { + // Share Option + com.sameerasw.essentials.ui.components.menus.SegmentedDropdownMenuItem( + text = { Text(stringResource(R.string.action_share)) }, + leadingIcon = { + Icon( + painter = androidx.compose.ui.res.painterResource(R.drawable.rounded_share_24), + contentDescription = null, + modifier = Modifier.size(20.dp) + ) + }, + onClick = { + showMenu = false + initialUri.let { uri -> + viewModel.shareImage(uri) { sharedUri -> + val shareIntent = android.content.Intent(android.content.Intent.ACTION_SEND).apply { + type = "image/jpeg" + putExtra(android.content.Intent.EXTRA_STREAM, sharedUri) + addFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + context.startActivity(android.content.Intent.createChooser(shareIntent, context.getString(R.string.action_share))) + } + } + }, + enabled = saveState !is WatermarkUiState.Processing + ) + + // Save Option + com.sameerasw.essentials.ui.components.menus.SegmentedDropdownMenuItem( + text = { Text(stringResource(R.string.action_save)) }, + leadingIcon = { + Icon( + painter = androidx.compose.ui.res.painterResource(R.drawable.rounded_save_24), + contentDescription = null, + modifier = Modifier.size(20.dp) + ) + }, + onClick = { + showMenu = false + viewModel.saveImage(initialUri) + }, + enabled = saveState !is WatermarkUiState.Processing + ) + } + } + } + } + ) + }, + containerColor = MaterialTheme.colorScheme.surfaceContainer + ) { padding -> + val density = androidx.compose.ui.platform.LocalDensity.current + val configuration = androidx.compose.ui.platform.LocalConfiguration.current + val screenHeightDp = configuration.screenHeightDp.dp + + val maxPreviewHeightDp = screenHeightDp * 0.6f + val minPreviewHeightDp = screenHeightDp * 0.4f + + val maxPx = with(density) { maxPreviewHeightDp.toPx() } + val minPx = with(density) { minPreviewHeightDp.toPx() } + + var previewHeightPx by androidx.compose.runtime.remember { androidx.compose.runtime.mutableFloatStateOf(maxPx) } + + val nestedScrollConnection = androidx.compose.runtime.remember { + object : androidx.compose.ui.input.nestedscroll.NestedScrollConnection { + override fun onPreScroll(available: androidx.compose.ui.geometry.Offset, source: androidx.compose.ui.input.nestedscroll.NestedScrollSource): androidx.compose.ui.geometry.Offset { + val delta = available.y + // Swiping Up (delta < 0): Collapse + if (delta < 0) { + val newHeight = (previewHeightPx + delta).coerceIn(minPx, maxPx) + val consumed = newHeight - previewHeightPx + previewHeightPx = newHeight + return androidx.compose.ui.geometry.Offset(0f, consumed) + } + return androidx.compose.ui.geometry.Offset.Zero + } + + override fun onPostScroll(consumed: androidx.compose.ui.geometry.Offset, available: androidx.compose.ui.geometry.Offset, source: androidx.compose.ui.input.nestedscroll.NestedScrollSource): androidx.compose.ui.geometry.Offset { + val delta = available.y + // Swiping Down (delta > 0): Expand + if (delta > 0) { + val newHeight = (previewHeightPx + delta).coerceIn(minPx, maxPx) + val consumedY = newHeight - previewHeightPx + previewHeightPx = newHeight + return androidx.compose.ui.geometry.Offset(0f, consumedY) + } + return androidx.compose.ui.geometry.Offset.Zero + } + } + } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(padding) + .nestedScroll(nestedScrollConnection) + ) { + // Preview Area (Variable Height) + Box( + modifier = Modifier + .fillMaxWidth() + .height(with(density) { previewHeightPx.toDp() }) + .padding(16.dp) + .clip(RoundedCornerShape(24.dp)) + .background(MaterialTheme.colorScheme.surfaceContainerHigh) + .clickable { + if (initialUri == null) { + onPickImage() + } else { + viewModel.toggleContrast() + } + } + .padding(if (initialUri == null) 32.dp else 0.dp), + contentAlignment = Alignment.Center + ) { + if (initialUri == null) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Icon( + painter = androidx.compose.ui.res.painterResource(R.drawable.rounded_add_photo_alternate_24), + contentDescription = null, + modifier = Modifier.size(64.dp), + tint = MaterialTheme.colorScheme.primary + ) + Spacer(Modifier.size(8.dp)) + Text( + stringResource(R.string.watermark_pick_image), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } else { + + + // Implementing the "Last Success Persist" logic here locally + val current = previewState + var lastSuccess by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(null) } + + if (current is WatermarkUiState.Success) { + lastSuccess = current + } + + val showBlur = current is WatermarkUiState.Processing + + val blurRadius by androidx.compose.animation.core.animateDpAsState( + targetValue = if (showBlur) 16.dp else 0.dp, + label = "blur" + ) + + val alpha by androidx.compose.animation.core.animateFloatAsState( + targetValue = if (showBlur) 0.6f else 1f, + label = "alpha" + ) + + Box(contentAlignment = Alignment.Center) { + // Underlying Image + if (lastSuccess != null) { + Box( + modifier = Modifier + .blur(blurRadius) + .alpha(alpha) + ) { + WatermarkPreview(uiState = lastSuccess!!) + } + } else { + // First load? + if (current is WatermarkUiState.Processing) { + // First load, show nothing or placeholder maybe + } else { + WatermarkPreview(uiState = current) + } + } + + // Overlay + androidx.compose.animation.AnimatedVisibility( + visible = showBlur, + enter = androidx.compose.animation.fadeIn(), + exit = androidx.compose.animation.fadeOut() + ) { + LoadingIndicator() + } + } + } + } + + // Allow this part to take remaining space and scroll + Box( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + ) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(androidx.compose.foundation.rememberScrollState()) + .padding(horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // Controls Area + RoundedCardContainer( + modifier = Modifier.fillMaxWidth() + ) { + // Style Picker + SegmentedPicker( + items = WatermarkStyle.entries, + selectedItem = options.style, + onItemSelected = { viewModel.setStyle(it) }, + labelProvider = { style -> + when (style) { + WatermarkStyle.OVERLAY -> context.getString(R.string.watermark_style_overlay) + WatermarkStyle.FRAME -> context.getString(R.string.watermark_style_frame) + } + }, + iconProvider = { style -> + val iconRes = when (style) { + WatermarkStyle.OVERLAY -> R.drawable.rounded_magnify_fullscreen_24 + WatermarkStyle.FRAME -> R.drawable.rounded_window_open_24 + } + + Icon( + painter = androidx.compose.ui.res.painterResource(id = iconRes), + contentDescription = null, + modifier = Modifier.size(18.dp) + ) + }, + modifier = Modifier.fillMaxWidth() + ) + + // Show Brand Toggle + IconToggleItem( + iconRes = R.drawable.rounded_info_24, + title = stringResource(R.string.watermark_show_brand), + isChecked = options.showDeviceBrand, + onCheckedChange = { viewModel.setShowBrand(it) } + ) + + // Show EXIF Toggle + IconToggleItem( + iconRes = R.drawable.rounded_info_24, + title = stringResource(R.string.watermark_show_exif), + isChecked = options.showExif, + onCheckedChange = { viewModel.setShowExif(it) } + ) + } + + // Bottom spacing for scrolling + Spacer(Modifier.height(24.dp)) + } + } + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt new file mode 100644 index 00000000..6561df51 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt @@ -0,0 +1,272 @@ +package com.sameerasw.essentials.viewmodels + +import android.content.Context +import android.net.Uri +import android.widget.Toast +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.sameerasw.essentials.R +import com.sameerasw.essentials.domain.watermark.MetadataProvider +import com.sameerasw.essentials.domain.watermark.WatermarkEngine +import com.sameerasw.essentials.domain.watermark.WatermarkOptions +import com.sameerasw.essentials.domain.watermark.WatermarkRepository +import com.sameerasw.essentials.domain.watermark.WatermarkStyle +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import java.io.File + +sealed class WatermarkUiState { + data object Idle : WatermarkUiState() + data object Processing : WatermarkUiState() + data class Success(val file: File) : WatermarkUiState() + data class Error(val message: String) : WatermarkUiState() +} + +class WatermarkViewModel( + private val watermarkEngine: WatermarkEngine, + private val watermarkRepository: WatermarkRepository, + private val context: Context +) : ViewModel() { + + companion object { + fun provideFactory(context: Context): ViewModelProvider.Factory = object : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + val appContext = context.applicationContext + val metadataProvider = MetadataProvider(appContext) + val engine = WatermarkEngine(appContext, metadataProvider) + val repository = WatermarkRepository(appContext) + return WatermarkViewModel(engine, repository, appContext) as T + } + } + } + + private val _uiState = MutableStateFlow(WatermarkUiState.Idle) + val uiState: StateFlow = _uiState.asStateFlow() + + private val _previewUiState = MutableStateFlow(WatermarkUiState.Idle) + val previewUiState: StateFlow = _previewUiState.asStateFlow() + + private val _options = MutableStateFlow(WatermarkOptions()) + val options: StateFlow = _options.asStateFlow() + + private var previewSourceBitmap: android.graphics.Bitmap? = null + private var currentUri: Uri? = null + + init { + viewModelScope.launch { + watermarkRepository.watermarkOptions.collectLatest { savedOptions -> + _options.value = savedOptions + updatePreview() + } + } + } + + fun loadPreview(uri: Uri) { + currentUri = uri + viewModelScope.launch(kotlinx.coroutines.Dispatchers.IO) { + try { + // Decode scaled version + val inputStream = context.contentResolver.openInputStream(uri) + val options = android.graphics.BitmapFactory.Options() + options.inJustDecodeBounds = true + android.graphics.BitmapFactory.decodeStream(inputStream, null, options) + inputStream?.close() + + // Calculate sample size to fit around 1080p + val reqWidth = 1080 + val reqHeight = 1080 + var inSampleSize = 1 + if (options.outHeight > reqHeight || options.outWidth > reqWidth) { + val halfHeight: Int = options.outHeight / 2 + val halfWidth: Int = options.outWidth / 2 + while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2 + } + } + + val decodeOptions = android.graphics.BitmapFactory.Options().apply { + this.inSampleSize = inSampleSize + this.inMutable = true // Ensure mutable + } + + val is2 = context.contentResolver.openInputStream(uri) + val bitmap = android.graphics.BitmapFactory.decodeStream(is2, null, decodeOptions) + is2?.close() + + if (bitmap != null) { + previewSourceBitmap = bitmap + updatePreview() + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + private fun updatePreview() { + val bitmap = previewSourceBitmap ?: return + val uri = currentUri ?: return + viewModelScope.launch { + _previewUiState.value = WatermarkUiState.Processing + try { + kotlinx.coroutines.delay(600) + val workingBitmap = bitmap.copy(bitmap.config ?: android.graphics.Bitmap.Config.ARGB_8888, true) + + val result = watermarkEngine.processBitmap(workingBitmap, uri, _options.value) + + val timestamp = System.currentTimeMillis() + val file = File(context.cacheDir, "preview_watermark_$timestamp.jpg") + val out = java.io.FileOutputStream(file) + result.compress(android.graphics.Bitmap.CompressFormat.JPEG, 80, out) + out.close() + + (_previewUiState.value as? WatermarkUiState.Success)?.file?.let { oldFile -> + if (oldFile.exists() && oldFile.name.startsWith("preview_watermark_")) { + oldFile.delete() + } + } + + _previewUiState.value = WatermarkUiState.Success(file) + } catch (e: Exception) { + e.printStackTrace() + _previewUiState.value = WatermarkUiState.Error(e.message ?: "Unknown error") + } + } + } + + fun setStyle(style: WatermarkStyle) { + viewModelScope.launch { + watermarkRepository.updateStyle(style) + } + } + + fun setShowBrand(show: Boolean) { + viewModelScope.launch { + watermarkRepository.updateShowBrand(show) + } + } + + fun setShowExif(show: Boolean) { + viewModelScope.launch { + watermarkRepository.updateShowExif(show) + } + } + + fun toggleContrast() { + viewModelScope.launch { + watermarkRepository.updateUseDarkTheme(!_options.value.useDarkTheme) + } + } + + fun saveImage(uri: Uri) { + viewModelScope.launch { + _uiState.value = WatermarkUiState.Processing + try { + // Process image to a temporary file first + val tempFile = watermarkEngine.processImage(uri, _options.value) + + // Save to MediaStore (Gallery) + val values = android.content.ContentValues().apply { + put( + android.provider.MediaStore.Images.Media.DISPLAY_NAME, + "WM_${System.currentTimeMillis()}.jpg" + ) + put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg") + // RELATIVE_PATH is available on Android 10+ (API 29) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + put( + android.provider.MediaStore.Images.Media.RELATIVE_PATH, + "Pictures/Essentials" + ) + } + } + + val resolver = context.contentResolver + val collection = + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + android.provider.MediaStore.Images.Media.getContentUri(android.provider.MediaStore.VOLUME_EXTERNAL_PRIMARY) + } else { + android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI + } + + val resultUri = resolver.insert(collection, values) + + if (resultUri != null) { + resolver.openOutputStream(resultUri)?.use { outStream -> + tempFile.inputStream().use { inStream -> + inStream.copyTo(outStream) + } + } + _uiState.value = + WatermarkUiState.Success(tempFile) // Or success with URI? State expects File, but it's just for success message. + } else { + throw Exception("Failed to create MediaStore entry") + } + } catch (e: Exception) { + e.printStackTrace() + _uiState.value = WatermarkUiState.Error(e.message ?: "Unknown error") + } + } + } + + fun shareImage(uri: Uri, onShareReady: (Uri) -> Unit) { + viewModelScope.launch { + _uiState.value = WatermarkUiState.Processing + try { + // Process image to a temporary file + val tempFile = watermarkEngine.processImage(uri, _options.value) + val savedUri = saveToMediaStore(tempFile) + if (savedUri != null) { + _uiState.value = WatermarkUiState.Idle + onShareReady(savedUri) + } else { + _uiState.value = WatermarkUiState.Error("Failed to prepare image for sharing") + } + } catch (e: Exception) { + e.printStackTrace() + _uiState.value = WatermarkUiState.Error(e.message ?: "Unknown error") + } + } + } + + private fun saveToMediaStore(sourceFile: File): Uri? { + try { + val values = android.content.ContentValues().apply { + put(android.provider.MediaStore.Images.Media.DISPLAY_NAME, "WM_SHARE_${System.currentTimeMillis()}.jpg") + put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg") + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + put(android.provider.MediaStore.Images.Media.RELATIVE_PATH, "Pictures/Essentials/Watermarks") + } + } + val resolver = context.contentResolver + val collection = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + android.provider.MediaStore.Images.Media.getContentUri(android.provider.MediaStore.VOLUME_EXTERNAL_PRIMARY) + } else { + android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI + } + + val resultUri = resolver.insert(collection, values) ?: return null + + resolver.openOutputStream(resultUri)?.use { outStream -> + sourceFile.inputStream().use { inStream -> + inStream.copyTo(outStream) + } + } + return resultUri + } catch (e: Exception) { + e.printStackTrace() + return null + } + } + + fun resetState() { + _uiState.value = WatermarkUiState.Idle + } +} diff --git a/app/src/main/res/drawable/rounded_add_photo_alternate_24.xml b/app/src/main/res/drawable/rounded_add_photo_alternate_24.xml new file mode 100644 index 00000000..0b44125a --- /dev/null +++ b/app/src/main/res/drawable/rounded_add_photo_alternate_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_window_open_24.xml b/app/src/main/res/drawable/rounded_window_open_24.xml new file mode 100644 index 00000000..08c6cc65 --- /dev/null +++ b/app/src/main/res/drawable/rounded_window_open_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d811709c..8859a7f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -324,6 +324,18 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermarks + Add EXIF data and logos to photos + Style + Overlay + Frame + Show Device Brand + Show EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share Widget Haptic feedback From d7ffb0e89661d1ab1c1fa810333b884b4abbbcb0 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 15:26:58 +0530 Subject: [PATCH 180/254] Update source file strings.xml --- app/src/main/res/values/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 947f7733..d811709c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -179,6 +179,12 @@ Active Inactive NFC + Private DNS + Toggle Private DNS mode + Auto + Off + USB Debugging + Toggle USB Debugging On Off @@ -370,6 +376,10 @@ Flashlight Pulse Toggle notification flashlight pulse Toggle stay awake developer option + Private DNS + Cycle Private DNS modes (Off/Auto/Hostname) + USB Debugging + Toggle USB Debugging developer option Enable Button Remap Master toggle for volume button remapping Remap Haptic Feedback From d694b6e57dce1169f6a969184a0cf8c05ce44b01 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 16:50:32 +0530 Subject: [PATCH 181/254] A lot of watermark options and controls added --- .../domain/watermark/WatermarkEngine.kt | 381 +++++++++++++++--- .../domain/watermark/WatermarkRepository.kt | 52 ++- .../ui/components/ReusableTopAppBar.kt | 5 +- .../composables/watermark/WatermarkScreen.kt | 214 +++++++++- .../viewmodels/WatermarkViewModel.kt | 42 ++ .../main/res/drawable/rounded_camera_24.xml | 5 + .../drawable/rounded_control_camera_24.xml | 5 + .../res/drawable/rounded_date_range_24.xml | 5 + .../main/res/drawable/rounded_grain_24.xml | 5 + .../res/drawable/rounded_image_search_24.xml | 5 + .../rounded_position_bottom_left_24.xml | 5 + .../res/drawable/rounded_shutter_speed_24.xml | 5 + .../drawable/rounded_top_panel_close_24.xml | 5 + app/src/main/res/values/strings.xml | 14 +- 14 files changed, 686 insertions(+), 62 deletions(-) create mode 100644 app/src/main/res/drawable/rounded_camera_24.xml create mode 100644 app/src/main/res/drawable/rounded_control_camera_24.xml create mode 100644 app/src/main/res/drawable/rounded_date_range_24.xml create mode 100644 app/src/main/res/drawable/rounded_grain_24.xml create mode 100644 app/src/main/res/drawable/rounded_image_search_24.xml create mode 100644 app/src/main/res/drawable/rounded_position_bottom_left_24.xml create mode 100644 app/src/main/res/drawable/rounded_shutter_speed_24.xml create mode 100644 app/src/main/res/drawable/rounded_top_panel_close_24.xml diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt index b211bc70..400071a1 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt @@ -26,9 +26,19 @@ data class WatermarkOptions( val style: WatermarkStyle = WatermarkStyle.FRAME, val showDeviceBrand: Boolean = true, val showExif: Boolean = true, + // Granular EXIF options + val showFocalLength: Boolean = true, + val showAperture: Boolean = true, + val showIso: Boolean = true, + val showShutterSpeed: Boolean = true, + val showDate: Boolean = false, val customText: String = "", val outputQuality: Int = 100, - val useDarkTheme: Boolean = false + val useDarkTheme: Boolean = false, + val moveToTop: Boolean = false, + val leftAlignOverlay: Boolean = false, + val brandTextSize: Int = 50, + val dataTextSize: Int = 50 ) class WatermarkEngine( @@ -134,13 +144,33 @@ class WatermarkEngine( val margin = bitmap.width * 0.05f var yPos = bitmap.height - margin + // Apply scaling + val brandScale = 0.5f + (options.brandTextSize / 100f) + val dataScale = 0.5f + (options.dataTextSize / 100f) + + // Base text size was 3% of width + val baseSize = bitmap.width * 0.03f + paint.textSize = baseSize * dataScale + if (options.showExif) { - val exifString = buildExifString(exifData) - if (exifString.isNotEmpty()) { - val textBounds = Rect() - paint.getTextBounds(exifString, 0, exifString.length, textBounds) - canvas.drawText(exifString, bitmap.width - margin - textBounds.width(), yPos, paint) - yPos -= textBounds.height() * 1.5f + val exifItems = buildExifList(exifData, options) + if (exifItems.isNotEmpty()) { + val maxWidth = bitmap.width - (margin * 2) + + // Wrap items with icons + val rows = wrapExifItems(exifItems, paint, maxWidth) + val reversedRows = rows.reversed() + + for (row in reversedRows) { + val rowWidth = measureRowWidth(row, paint) + val rowHeight = measureRowHeight(row, paint) + + var xPos = if (options.leftAlignOverlay) margin else (bitmap.width - margin - rowWidth) + + drawExifRow(canvas, row, xPos, yPos, paint, shadowColor) + + yPos -= rowHeight * 1.5f + } } } @@ -148,65 +178,251 @@ class WatermarkEngine( val brandString = buildBrandString(exifData) val brandPaint = Paint(paint).apply { typeface = Typeface.DEFAULT_BOLD + textSize = baseSize * brandScale // Use brand scale } val textBounds = Rect() brandPaint.getTextBounds(brandString, 0, brandString.length, textBounds) - canvas.drawText(brandString, bitmap.width - margin - textBounds.width(), yPos, brandPaint) + + val xPos = if (options.leftAlignOverlay) margin else (bitmap.width - margin - textBounds.width()) + + canvas.drawText(brandString, xPos, yPos, brandPaint) } return bitmap } private fun drawFrame(bitmap: Bitmap, exifData: ExifData, options: WatermarkOptions): Bitmap { - val frameHeight = (bitmap.height * 0.10f).roundToInt() // 10% chin - val newHeight = bitmap.height + frameHeight - - val finalBitmap = Bitmap.createBitmap(bitmap.width, newHeight, Bitmap.Config.ARGB_8888) - val canvas = Canvas(finalBitmap) + var baseFrameHeight = (bitmap.height * 0.10f).roundToInt() val useDark = options.useDarkTheme val bgColor = if (useDark) Color.BLACK else Color.WHITE val textColor = if (useDark) Color.WHITE else Color.BLACK val secondaryTextColor = if (useDark) Color.LTGRAY else Color.GRAY + + // Setup paints early to measure + // Setup paints early to measure + val brandScale = 0.5f + (options.brandTextSize / 100f) + val dataScale = 0.5f + (options.dataTextSize / 100f) + + val brandPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = textColor + textSize = (baseFrameHeight * 0.3f) * brandScale + typeface = Typeface.DEFAULT_BOLD + } + val exifPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = secondaryTextColor + textSize = (baseFrameHeight * 0.2f) * dataScale + } + + val margin = bitmap.width * 0.05f + + val maxAvailableWidth = if (options.showDeviceBrand) { + (bitmap.width - margin * 2) * 0.6f + } else { + (bitmap.width - margin * 2).toFloat() + } + + var exifRows: List> = emptyList() + var totalExifHeight = 0f + + if (options.showExif) { + val exifItems = buildExifList(exifData, options) + if (exifItems.isNotEmpty()) { + exifRows = wrapExifItems(exifItems, exifPaint, maxAvailableWidth) + totalExifHeight = exifRows.size * (exifPaint.textSize * 1.5f) + } + } + + // Dynamic Height Calculation + val requiredHeight = max( + brandPaint.textSize * 2.5f, + totalExifHeight + (exifPaint.textSize * 2f) + ).roundToInt() + + val finalFrameHeight = max(baseFrameHeight, requiredHeight) + val newHeight = bitmap.height + finalFrameHeight + + val finalBitmap = Bitmap.createBitmap(bitmap.width, newHeight, Bitmap.Config.ARGB_8888) + val canvas = Canvas(finalBitmap) + // Draw background canvas.drawColor(bgColor) - // Draw original image - canvas.drawBitmap(bitmap, 0f, 0f, null) + // Draw Image and Text + if (options.moveToTop) { + // Draw Image shifted down by frameHeight + canvas.drawBitmap(bitmap, 0f, finalFrameHeight.toFloat(), null) + + // Draw Text in "Forehead" + val centerY = finalFrameHeight / 2f + drawFrameContent( + canvas, exifData, options, margin, centerY, + brandPaint, exifPaint, exifRows, bitmap.width + ) + + } else { + // Draw Image at 0,0 + canvas.drawBitmap(bitmap, 0f, 0f, null) + + // Draw Text in "Chin" + val centerY = bitmap.height + (finalFrameHeight / 2f) + drawFrameContent( + canvas, exifData, options, margin, centerY, + brandPaint, exifPaint, exifRows, bitmap.width + ) + } + + return finalBitmap + } + + private fun drawFrameContent( + canvas: Canvas, exifData: ExifData, options: WatermarkOptions, + margin: Float, centerY: Float, + brandPaint: Paint, exifPaint: Paint, + exifRows: List>, canvasWidth: Int + + ) { - // Draw Text in Chin - val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - color = textColor - textSize = frameHeight * 0.25f + // Brand on Left + if (options.showDeviceBrand) { + val brandString = buildBrandString(exifData) + // Vertical center: centerY + half text height (as baseline) + val brandY = centerY + (brandPaint.textSize / 3f) + canvas.drawText(brandString, margin, brandY, brandPaint) } - val leftMargin = bitmap.width * 0.05f - val centerY = bitmap.height + (frameHeight / 2f) + (paint.textSize / 3f) + // Exif on Right + if (options.showExif && exifRows.isNotEmpty()) { + val lineHeight = exifPaint.textSize * 1.5f + + val centeringOffset = (exifRows.size - 1) * lineHeight / 2f + var currentY = (centerY + exifPaint.textSize / 3f) - centeringOffset + + for (row in exifRows) { + val rowWidth = measureRowWidth(row, exifPaint) + val xPos = canvasWidth - margin - rowWidth + drawExifRow(canvas, row, xPos, currentY, exifPaint, null) + currentY += lineHeight + } + } + } + + private fun wrapExifItems(items: List, paint: Paint, maxWidth: Float): List> { + val rows = mutableListOf>() + if (items.isEmpty()) return rows + + var currentRow = mutableListOf() + var currentWidth = 0f + val separatorWidth = 0f + val itemSpacing = paint.textSize * 0.8f - // Left side: Brand / Model - if (options.showDeviceBrand) { - val brandPaint = Paint(paint).apply { - typeface = Typeface.DEFAULT_BOLD - textSize = frameHeight * 0.3f + for (item in items) { + val itemWidth = measureItemWidth(item, paint) + + if (currentRow.isEmpty()) { + currentRow.add(item) + currentWidth += itemWidth + } else { + if (currentWidth + itemSpacing + itemWidth <= maxWidth) { + currentRow.add(item) + currentWidth += itemSpacing + itemWidth + } else { + rows.add(currentRow) + currentRow = mutableListOf(item) + currentWidth = itemWidth + } } - val brandString = buildBrandString(exifData) - canvas.drawText(brandString, leftMargin, centerY, brandPaint) } + if (currentRow.isNotEmpty()) { + rows.add(currentRow) + } + return rows + } + + private fun measureItemWidth(item: ExifItem, paint: Paint): Float { + // Icon + Padding + Text + val iconSize = paint.textSize * 1.2f + val padding = paint.textSize * 0.4f + val textWidth = paint.measureText(item.text) + return iconSize + padding + textWidth + } + + private fun measureRowWidth(row: List, paint: Paint): Float { + var width = 0f + val itemSpacing = paint.textSize * 0.8f + for (i in row.indices) { + width += measureItemWidth(row[i], paint) + if (i < row.size - 1) width += itemSpacing + } + return width + } + + private fun measureRowHeight(row: List, paint: Paint): Float { + return paint.textSize * 1.5f // Use standard height + } - // Right side: EXIF - if (options.showExif) { - val exifString = buildExifString(exifData) - val exifPaint = Paint(paint).apply { - color = secondaryTextColor - textSize = frameHeight * 0.2f + private fun drawExifRow( + canvas: Canvas, row: List, + xStart: Float, yPos: Float, + paint: Paint, shadowColor: Int? + ) { + var currentX = xStart + val iconSize = paint.textSize * 1.2f + val padding = paint.textSize * 0.4f + val itemSpacing = paint.textSize * 0.8f + + val iconY = yPos - (paint.textSize / 2f) - (iconSize / 2f) + + for (item in row) { + // Draw Icon + val iconBitmap = loadVectorBitmap(context, item.iconRes, paint.color) + if (iconBitmap != null) { + val destRect = Rect( + currentX.toInt(), + iconY.toInt(), + (currentX + iconSize).toInt(), + (iconY + iconSize).toInt() + ) + + if (shadowColor != null) { + val shadowPaint = Paint(paint).apply { + color = shadowColor + colorFilter = android.graphics.PorterDuffColorFilter(shadowColor, android.graphics.PorterDuff.Mode.SRC_IN) + } + val shadowRect = Rect(destRect) + shadowRect.offset(2, 2) + canvas.drawBitmap(iconBitmap, null, shadowRect, shadowPaint) + } + + canvas.drawBitmap(iconBitmap, null, destRect, null) // Already tinted if we created it tinted } - val textBounds = Rect() - exifPaint.getTextBounds(exifString, 0, exifString.length, textBounds) - canvas.drawText(exifString, bitmap.width - leftMargin - textBounds.width(), centerY, exifPaint) + + currentX += iconSize + padding + + // Draw Text + canvas.drawText(item.text, currentX, yPos, paint) + + currentX += paint.measureText(item.text) + itemSpacing } + } - return finalBitmap + // Cache for bitmaps + private val iconCache = mutableMapOf() + + private fun loadVectorBitmap(context: Context, resId: Int, color: Int): Bitmap? { + + try { + val drawable = androidx.core.content.ContextCompat.getDrawable(context, resId) ?: return null + val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + drawable.setBounds(0, 0, canvas.width, canvas.height) + drawable.setTint(color) + drawable.draw(canvas) + return bitmap + } catch (e: Exception) { + return null + } } private fun buildBrandString(exif: ExifData): String { @@ -221,12 +437,87 @@ class WatermarkEngine( } } - private fun buildExifString(exif: ExifData): String { - val parts = mutableListOf() - exif.focalLength?.let { parts.add(it) } - exif.aperture?.let { parts.add(it) } - exif.shutterSpeed?.let { parts.add(it) } - exif.iso?.let { parts.add(it) } - return parts.joinToString(" • ") + private data class ExifItem(val text: String, val iconRes: Int) + + private fun buildExifList(exif: ExifData, options: WatermarkOptions): List { + val list = mutableListOf() + + if (options.showFocalLength) exif.focalLength?.let { + list.add(ExifItem(it, R.drawable.rounded_control_camera_24)) + } + if (options.showAperture) exif.aperture?.let { + list.add(ExifItem(it, R.drawable.rounded_camera_24)) + } + if (options.showShutterSpeed) exif.shutterSpeed?.let { + list.add(ExifItem(formatShutterSpeed(it), R.drawable.rounded_shutter_speed_24)) + } + if (options.showIso) exif.iso?.let { + list.add(ExifItem(it, R.drawable.rounded_grain_24)) + } + if (options.showDate) exif.date?.let { + list.add(ExifItem(formatDate(it), R.drawable.rounded_date_range_24)) + } + + return list + } + + private fun formatDate(dateString: String): String { + try { + // Input format: yyyy:MM:dd HH:mm:ss + val inputFormat = java.text.SimpleDateFormat("yyyy:MM:dd HH:mm:ss", java.util.Locale.US) + val date = inputFormat.parse(dateString) ?: return dateString + + // Output format components + val dayFormat = java.text.SimpleDateFormat("d", java.util.Locale.US) + val monthYearFormat = java.text.SimpleDateFormat("MMM yyyy", java.util.Locale.US) + + // Use system time format (12/24h) + val timeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT) + + val day = dayFormat.format(date).toInt() + val suffix = getDaySuffix(day) + + return "$day$suffix ${monthYearFormat.format(date)}, ${timeFormat.format(date)}" + } catch (e: Exception) { + return dateString + } + } + + private fun getDaySuffix(n: Int): String { + if (n in 11..13) return "th" + return when (n % 10) { + 1 -> "st" + 2 -> "nd" + 3 -> "rd" + else -> "th" + } + } + + private fun formatShutterSpeed(raw: String): String { + // raw usually comes as "0.02s" or "1/100s" from MetadataProvider due to appended "s" in provider + // but if we are robust, we check. + val value = raw.removeSuffix("s") + // If it's a fraction, keep it (photographers prefer fractions) + if (value.contains("/")) return raw + + return try { + val doubleVal = value.toDouble() + // Round to max 2 decimals + // usage of %.2f might result in 0.00 for very fast speeds? + // User asked "maximum of 2 decimals", implying checking if it has more. + // But if it is 0.0005, 0.00 is bad. + // Maybe they mean for long exposures e.g. 2.534s -> 0.53s. + // Let's assume standard formatting. + if (doubleVal >= 1 || doubleVal == 0.0) { + java.lang.String.format(java.util.Locale.US, "%.2fs", doubleVal).removeSuffix(".00s").removeSuffix("0s") + "s" + } else { + // Formatting small decimals + // user request: "round to maximum of 2 decimals" + // If 0.016 -> 0.02s + java.lang.String.format(java.util.Locale.US, "%.2fs", doubleVal) + } + } catch (e: Exception) { + raw + } } } diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt index f50f2960..6293e993 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt @@ -17,7 +17,16 @@ class WatermarkRepository( private val PREF_STYLE = stringPreferencesKey("watermark_style") private val PREF_SHOW_BRAND = booleanPreferencesKey("show_brand") private val PREF_SHOW_EXIF = booleanPreferencesKey("show_exif") + private val PREF_SHOW_FOCAL_LENGTH = booleanPreferencesKey("show_focal_length") + private val PREF_SHOW_APERTURE = booleanPreferencesKey("show_aperture") + private val PREF_SHOW_ISO = booleanPreferencesKey("show_iso") + private val PREF_SHOW_SHUTTER = booleanPreferencesKey("show_shutter") + private val PREF_SHOW_DATE = booleanPreferencesKey("show_date") private val PREF_USE_DARK_THEME = booleanPreferencesKey("use_dark_theme") + private val PREF_MOVE_TO_TOP = booleanPreferencesKey("move_to_top") + private val PREF_LEFT_ALIGN = booleanPreferencesKey("left_align") + private val PREF_BRAND_TEXT_SIZE = androidx.datastore.preferences.core.intPreferencesKey("brand_text_size") + private val PREF_DATA_TEXT_SIZE = androidx.datastore.preferences.core.intPreferencesKey("data_text_size") val watermarkOptions: Flow = context.dataStore.data .map { preferences -> @@ -32,7 +41,16 @@ class WatermarkRepository( style = style, showDeviceBrand = preferences[PREF_SHOW_BRAND] ?: true, showExif = preferences[PREF_SHOW_EXIF] ?: true, - useDarkTheme = preferences[PREF_USE_DARK_THEME] ?: false + showFocalLength = preferences[PREF_SHOW_FOCAL_LENGTH] ?: true, + showAperture = preferences[PREF_SHOW_APERTURE] ?: true, + showIso = preferences[PREF_SHOW_ISO] ?: true, + showShutterSpeed = preferences[PREF_SHOW_SHUTTER] ?: true, + showDate = preferences[PREF_SHOW_DATE] ?: false, + useDarkTheme = preferences[PREF_USE_DARK_THEME] ?: false, + moveToTop = preferences[PREF_MOVE_TO_TOP] ?: false, + leftAlignOverlay = preferences[PREF_LEFT_ALIGN] ?: false, + brandTextSize = preferences[PREF_BRAND_TEXT_SIZE] ?: 50, + dataTextSize = preferences[PREF_DATA_TEXT_SIZE] ?: 50 ) } @@ -47,8 +65,40 @@ class WatermarkRepository( suspend fun updateShowExif(show: Boolean) { context.dataStore.edit { it[PREF_SHOW_EXIF] = show } } + + suspend fun updateExifSettings( + focalLength: Boolean, + aperture: Boolean, + iso: Boolean, + shutterSpeed: Boolean, + date: Boolean + ) { + context.dataStore.edit { + it[PREF_SHOW_FOCAL_LENGTH] = focalLength + it[PREF_SHOW_APERTURE] = aperture + it[PREF_SHOW_ISO] = iso + it[PREF_SHOW_SHUTTER] = shutterSpeed + it[PREF_SHOW_DATE] = date + } + } suspend fun updateUseDarkTheme(useDark: Boolean) { context.dataStore.edit { it[PREF_USE_DARK_THEME] = useDark } } + + suspend fun updateMoveToTop(move: Boolean) { + context.dataStore.edit { it[PREF_MOVE_TO_TOP] = move } + } + + suspend fun updateLeftAlign(left: Boolean) { + context.dataStore.edit { it[PREF_LEFT_ALIGN] = left } + } + + suspend fun updateBrandTextSize(size: Int) { + context.dataStore.edit { it[PREF_BRAND_TEXT_SIZE] = size } + } + + suspend fun updateDataTextSize(size: Int) { + context.dataStore.edit { it[PREF_DATA_TEXT_SIZE] = size } + } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/ReusableTopAppBar.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/ReusableTopAppBar.kt index 055fc628..405a1b42 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/ReusableTopAppBar.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/ReusableTopAppBar.kt @@ -53,6 +53,7 @@ fun ReusableTopAppBar( isBeta: Boolean = false, backIconRes: Int = R.drawable.rounded_arrow_back_24, isSmall: Boolean = false, + containerColor: Color = MaterialTheme.colorScheme.surfaceContainer, actions: @Composable RowScope.() -> Unit = {} ) { val collapsedFraction = scrollBehavior?.state?.collapsedFraction ?: 0f @@ -241,7 +242,7 @@ fun ReusableTopAppBar( if (isSmall) { TopAppBar( colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.surfaceContainer + containerColor = containerColor ), modifier = Modifier.padding(horizontal = 8.dp), title = titleContent, @@ -252,7 +253,7 @@ fun ReusableTopAppBar( } else { LargeFlexibleTopAppBar( colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.surfaceContainer + containerColor = containerColor ), modifier = Modifier.padding(horizontal = 8.dp), expandedHeight = if (subtitle != null) 200.dp else 160.dp, diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt index a8e28a76..40d8fbef 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt @@ -49,6 +49,9 @@ import com.sameerasw.essentials.ui.components.ReusableTopAppBar import com.sameerasw.essentials.ui.components.cards.IconToggleItem import com.sameerasw.essentials.ui.components.containers.RoundedCardContainer import com.sameerasw.essentials.ui.components.pickers.SegmentedPicker +import com.sameerasw.essentials.ui.components.sliders.ConfigSliderItem +import com.sameerasw.essentials.utils.HapticUtil.performSliderHaptic +import com.sameerasw.essentials.utils.HapticUtil.performUIHaptic import com.sameerasw.essentials.viewmodels.WatermarkUiState import com.sameerasw.essentials.viewmodels.WatermarkViewModel @@ -61,6 +64,9 @@ fun WatermarkScreen( viewModel: WatermarkViewModel ) { val context = LocalContext.current + val view = androidx.compose.ui.platform.LocalView.current // For haptics + var showExifSheet by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(false) } + val options by viewModel.options.collectAsState() val previewState by viewModel.previewUiState.collectAsState() val saveState by viewModel.uiState.collectAsState() @@ -90,14 +96,21 @@ fun WatermarkScreen( ReusableTopAppBar( title = R.string.feat_watermark_title, hasBack = true, - onBackClick = onBack, + onBackClick = { + performUIHaptic(view) + onBack() + }, isSmall = true, + containerColor = MaterialTheme.colorScheme.background, actions = { val pickImageButton = @Composable { // Pick Image Button if (initialUri == null) { // Primary when no image - androidx.compose.material3.Button(onClick = onPickImage) { + androidx.compose.material3.Button(onClick = { + performUIHaptic(view) + onPickImage() + }) { Icon( painter = androidx.compose.ui.res.painterResource(R.drawable.rounded_add_photo_alternate_24), contentDescription = stringResource(R.string.watermark_pick_image), @@ -108,7 +121,10 @@ fun WatermarkScreen( } } else { // Secondary when image is there - androidx.compose.material3.OutlinedButton(onClick = onPickImage) { + androidx.compose.material3.OutlinedButton(onClick = { + performUIHaptic(view) + onPickImage() + }) { Icon( painter = androidx.compose.ui.res.painterResource(R.drawable.rounded_add_photo_alternate_24), contentDescription = stringResource(R.string.watermark_pick_image), @@ -127,7 +143,10 @@ fun WatermarkScreen( Box { // Save Button (Primary) - androidx.compose.material3.Button(onClick = { showMenu = true }) { + androidx.compose.material3.Button(onClick = { + performUIHaptic(view) + showMenu = true + }) { Icon( painter = androidx.compose.ui.res.painterResource(R.drawable.rounded_save_24), contentDescription = stringResource(R.string.action_save), @@ -189,7 +208,7 @@ fun WatermarkScreen( } ) }, - containerColor = MaterialTheme.colorScheme.surfaceContainer + containerColor = MaterialTheme.colorScheme.background ) { padding -> val density = androidx.compose.ui.platform.LocalDensity.current val configuration = androidx.compose.ui.platform.LocalConfiguration.current @@ -211,6 +230,9 @@ fun WatermarkScreen( if (delta < 0) { val newHeight = (previewHeightPx + delta).coerceIn(minPx, maxPx) val consumed = newHeight - previewHeightPx + if (kotlin.math.abs(consumed) > 0.5f) { + performSliderHaptic(view) + } previewHeightPx = newHeight return androidx.compose.ui.geometry.Offset(0f, consumed) } @@ -223,6 +245,9 @@ fun WatermarkScreen( if (delta > 0) { val newHeight = (previewHeightPx + delta).coerceIn(minPx, maxPx) val consumedY = newHeight - previewHeightPx + if (kotlin.math.abs(consumedY) > 0.5f) { + performSliderHaptic(view) + } previewHeightPx = newHeight return androidx.compose.ui.geometry.Offset(0f, consumedY) } @@ -246,6 +271,7 @@ fun WatermarkScreen( .clip(RoundedCornerShape(24.dp)) .background(MaterialTheme.colorScheme.surfaceContainerHigh) .clickable { + performUIHaptic(view) if (initialUri == null) { onPickImage() } else { @@ -345,7 +371,10 @@ fun WatermarkScreen( SegmentedPicker( items = WatermarkStyle.entries, selectedItem = options.style, - onItemSelected = { viewModel.setStyle(it) }, + onItemSelected = { + performUIHaptic(view) + viewModel.setStyle(it) + }, labelProvider = { style -> when (style) { WatermarkStyle.OVERLAY -> context.getString(R.string.watermark_style_overlay) @@ -366,26 +395,187 @@ fun WatermarkScreen( }, modifier = Modifier.fillMaxWidth() ) + + // Style-specific options + if (options.style == WatermarkStyle.FRAME) { + com.sameerasw.essentials.ui.components.cards.IconToggleItem( + iconRes = R.drawable.rounded_top_panel_close_24, + title = stringResource(R.string.watermark_move_to_top), + isChecked = options.moveToTop, + onCheckedChange = { viewModel.setMoveToTop(it) } + ) + } else { + com.sameerasw.essentials.ui.components.cards.IconToggleItem( + iconRes = R.drawable.rounded_position_bottom_left_24, + title = stringResource(R.string.watermark_left_align), + isChecked = options.leftAlignOverlay, + onCheckedChange = { viewModel.setLeftAlign(it) } + ) + } // Show Brand Toggle IconToggleItem( - iconRes = R.drawable.rounded_info_24, + iconRes = R.drawable.rounded_mobile_text_2_24, title = stringResource(R.string.watermark_show_brand), isChecked = options.showDeviceBrand, onCheckedChange = { viewModel.setShowBrand(it) } ) - // Show EXIF Toggle - IconToggleItem( - iconRes = R.drawable.rounded_info_24, + // Show EXIF Settings (Custom Row with Chevron) + Row( + modifier = Modifier + .fillMaxWidth() + .background( + color = MaterialTheme.colorScheme.surfaceBright, + shape = RoundedCornerShape(MaterialTheme.shapes.extraSmall.bottomEnd) + ) + .heightIn(min = 56.dp) // Match standard item height + .clickable { + com.sameerasw.essentials.utils.HapticUtil.performUIHaptic(view) + showExifSheet = true + } + .padding(12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Spacer(modifier = Modifier.size(2.dp)) + Icon( + painter = androidx.compose.ui.res.painterResource(id = R.drawable.rounded_image_search_24), + contentDescription = stringResource(R.string.watermark_show_exif), + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.primary + ) + Spacer(modifier = Modifier.size(2.dp)) + + Text( + text = stringResource(R.string.watermark_show_exif), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.weight(1f) + ) + + Icon( + painter = androidx.compose.ui.res.painterResource(id = R.drawable.rounded_chevron_right_24), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + + // Text Size Sliders + if (options.showDeviceBrand) { + var brandSize by androidx.compose.runtime.remember(options.brandTextSize) { androidx.compose.runtime.mutableFloatStateOf(options.brandTextSize.toFloat()) } + + ConfigSliderItem( + title = stringResource(R.string.watermark_text_size_brand), + value = brandSize, + onValueChange = { + brandSize = it + com.sameerasw.essentials.utils.HapticUtil.performSliderHaptic(view) + }, + onValueChangeFinished = { viewModel.setBrandTextSize(brandSize.toInt()) }, + valueRange = 0f..100f, + increment = 5f, + valueFormatter = { "${it.toInt()}%" } + ) + } + + if (options.showExif) { + var dataSize by androidx.compose.runtime.remember(options.dataTextSize) { androidx.compose.runtime.mutableFloatStateOf(options.dataTextSize.toFloat()) } + + ConfigSliderItem( + title = stringResource(R.string.watermark_text_size_data), + value = dataSize, + onValueChange = { + dataSize = it + com.sameerasw.essentials.utils.HapticUtil.performSliderHaptic(view) + }, + onValueChangeFinished = { viewModel.setDataTextSize(dataSize.toInt()) }, + valueRange = 0f..100f, + increment = 5f, + valueFormatter = { "${it.toInt()}%" } + ) + } + } + + // Bottom spacing for scrolling + Spacer(Modifier.height(24.dp)) + } + } + } + + if (showExifSheet) { + val view = androidx.compose.ui.platform.LocalView.current + androidx.compose.material3.ModalBottomSheet( + onDismissRequest = { showExifSheet = false }, + containerColor = MaterialTheme.colorScheme.surfaceContainer + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + .padding(bottom = 32.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = stringResource(R.string.watermark_exif_settings), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(bottom = 8.dp) + ) + + RoundedCardContainer { + // Master Toggle + com.sameerasw.essentials.ui.components.cards.IconToggleItem( + iconRes = R.drawable.rounded_image_search_24, title = stringResource(R.string.watermark_show_exif), isChecked = options.showExif, onCheckedChange = { viewModel.setShowExif(it) } ) } - // Bottom spacing for scrolling - Spacer(Modifier.height(24.dp)) + if (options.showExif) { + RoundedCardContainer { + // Granular toggles + // Helper for granular + val updateExif = { focal: Boolean, aperture: Boolean, iso: Boolean, shutter: Boolean, date: Boolean -> + viewModel.setExifSettings(focal, aperture, iso, shutter, date) + } + + IconToggleItem( + iconRes = R.drawable.rounded_control_camera_24, + title = stringResource(R.string.watermark_exif_focal_length), + isChecked = options.showFocalLength, + onCheckedChange = { updateExif(it, options.showAperture, options.showIso, options.showShutterSpeed, options.showDate) } + ) + + IconToggleItem( + iconRes = R.drawable.rounded_camera_24, + title = stringResource(R.string.watermark_exif_aperture), + isChecked = options.showAperture, + onCheckedChange = { updateExif(options.showFocalLength, it, options.showIso, options.showShutterSpeed, options.showDate) } + ) + + IconToggleItem( + iconRes = R.drawable.rounded_grain_24, + title = stringResource(R.string.watermark_exif_iso), + isChecked = options.showIso, + onCheckedChange = { updateExif(options.showFocalLength, options.showAperture, it, options.showShutterSpeed, options.showDate) } + ) + + IconToggleItem( + iconRes = R.drawable.rounded_shutter_speed_24, + title = stringResource(R.string.watermark_exif_shutter_speed), + isChecked = options.showShutterSpeed, + onCheckedChange = { updateExif(options.showFocalLength, options.showAperture, options.showIso, it, options.showDate) } + ) + + IconToggleItem( + iconRes = R.drawable.rounded_date_range_24, + title = stringResource(R.string.watermark_exif_date), + isChecked = options.showDate, + onCheckedChange = { updateExif(options.showFocalLength, options.showAperture, options.showIso, options.showShutterSpeed, it) } + ) + } + } } } } diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt index 6561df51..2025ba04 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt @@ -159,12 +159,54 @@ class WatermarkViewModel( } } + fun setExifSettings( + focalLength: Boolean, + aperture: Boolean, + iso: Boolean, + shutterSpeed: Boolean, + date: Boolean + ) { + viewModelScope.launch { + watermarkRepository.updateExifSettings(focalLength, aperture, iso, shutterSpeed, date) + // Trigger preview update + previewSourceBitmap?.let { updatePreview() } + } + } + fun toggleContrast() { viewModelScope.launch { watermarkRepository.updateUseDarkTheme(!_options.value.useDarkTheme) } } + fun setMoveToTop(move: Boolean) { + viewModelScope.launch { + watermarkRepository.updateMoveToTop(move) + previewSourceBitmap?.let { updatePreview() } + } + } + + fun setLeftAlign(left: Boolean) { + viewModelScope.launch { + watermarkRepository.updateLeftAlign(left) + previewSourceBitmap?.let { updatePreview() } + } + } + + fun setBrandTextSize(size: Int) { + viewModelScope.launch { + watermarkRepository.updateBrandTextSize(size) + previewSourceBitmap?.let { updatePreview() } + } + } + + fun setDataTextSize(size: Int) { + viewModelScope.launch { + watermarkRepository.updateDataTextSize(size) + previewSourceBitmap?.let { updatePreview() } + } + } + fun saveImage(uri: Uri) { viewModelScope.launch { _uiState.value = WatermarkUiState.Processing diff --git a/app/src/main/res/drawable/rounded_camera_24.xml b/app/src/main/res/drawable/rounded_camera_24.xml new file mode 100644 index 00000000..3a01dc0c --- /dev/null +++ b/app/src/main/res/drawable/rounded_camera_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_control_camera_24.xml b/app/src/main/res/drawable/rounded_control_camera_24.xml new file mode 100644 index 00000000..a02edfd2 --- /dev/null +++ b/app/src/main/res/drawable/rounded_control_camera_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_date_range_24.xml b/app/src/main/res/drawable/rounded_date_range_24.xml new file mode 100644 index 00000000..52f34f06 --- /dev/null +++ b/app/src/main/res/drawable/rounded_date_range_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_grain_24.xml b/app/src/main/res/drawable/rounded_grain_24.xml new file mode 100644 index 00000000..1cb3e4b3 --- /dev/null +++ b/app/src/main/res/drawable/rounded_grain_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_image_search_24.xml b/app/src/main/res/drawable/rounded_image_search_24.xml new file mode 100644 index 00000000..1fc7cba9 --- /dev/null +++ b/app/src/main/res/drawable/rounded_image_search_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_position_bottom_left_24.xml b/app/src/main/res/drawable/rounded_position_bottom_left_24.xml new file mode 100644 index 00000000..a78a00cc --- /dev/null +++ b/app/src/main/res/drawable/rounded_position_bottom_left_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_shutter_speed_24.xml b/app/src/main/res/drawable/rounded_shutter_speed_24.xml new file mode 100644 index 00000000..6ab96847 --- /dev/null +++ b/app/src/main/res/drawable/rounded_shutter_speed_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_top_panel_close_24.xml b/app/src/main/res/drawable/rounded_top_panel_close_24.xml new file mode 100644 index 00000000..3116aa89 --- /dev/null +++ b/app/src/main/res/drawable/rounded_top_panel_close_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8859a7f1..52a4dae9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -329,13 +329,23 @@ Style Overlay Frame - Show Device Brand - Show EXIF Data + Device Brand + EXIF Data Pick Image Image saved to gallery Failed to save image Processing... Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size Widget Haptic feedback From 54958cb29d4b267599154efb522ab88a274e5ef7 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 16:54:44 +0530 Subject: [PATCH 182/254] Fix edge to edge in watermark --- .../essentials/ui/composables/watermark/WatermarkActivity.kt | 5 +++++ .../essentials/ui/composables/watermark/WatermarkScreen.kt | 1 + 2 files changed, 6 insertions(+) diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt index d1487121..b0d927de 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt @@ -9,6 +9,7 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.view.WindowCompat import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.getValue @@ -37,8 +38,12 @@ class WatermarkActivity : ComponentActivity() { } override fun onCreate(savedInstanceState: Bundle?) { + WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) enableEdgeToEdge() + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + window.isNavigationBarContrastEnforced = false + } // Handle Share Intent if (intent?.action == Intent.ACTION_SEND && intent.type?.startsWith("image/") == true) { diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt index 40d8fbef..fedbce4f 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt @@ -92,6 +92,7 @@ fun WatermarkScreen( } Scaffold( + contentWindowInsets = androidx.compose.foundation.layout.WindowInsets(0, 0, 0, 0), topBar = { ReusableTopAppBar( title = R.string.feat_watermark_title, From def381bc4f50cbd8906c446da0fd4d56b57ddd19 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 17:19:16 +0530 Subject: [PATCH 183/254] Custom text in watermark --- .../domain/watermark/WatermarkEngine.kt | 104 +++++++++-- .../domain/watermark/WatermarkRepository.kt | 22 ++- .../composables/watermark/WatermarkScreen.kt | 174 ++++++++++++++++++ .../viewmodels/WatermarkViewModel.kt | 14 ++ .../res/drawable/rounded_edit_note_24.xml | 5 + app/src/main/res/values/strings.xml | 6 + 6 files changed, 308 insertions(+), 17 deletions(-) create mode 100644 app/src/main/res/drawable/rounded_edit_note_24.xml diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt index 400071a1..1d7f38fa 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt @@ -32,13 +32,16 @@ data class WatermarkOptions( val showIso: Boolean = true, val showShutterSpeed: Boolean = true, val showDate: Boolean = false, - val customText: String = "", val outputQuality: Int = 100, val useDarkTheme: Boolean = false, val moveToTop: Boolean = false, val leftAlignOverlay: Boolean = false, val brandTextSize: Int = 50, - val dataTextSize: Int = 50 + val dataTextSize: Int = 50, + val showCustomText: Boolean = false, + val customText: String = "", + val customTextSize: Int = 50, + val padding: Int = 50 ) class WatermarkEngine( @@ -141,7 +144,7 @@ class WatermarkEngine( setShadowLayer(4f, 2f, 2f, shadowColor) } - val margin = bitmap.width * 0.05f + val margin = bitmap.width * (options.padding / 1000f) var yPos = bitmap.height - margin // Apply scaling @@ -169,11 +172,34 @@ class WatermarkEngine( drawExifRow(canvas, row, xPos, yPos, paint, shadowColor) - yPos -= rowHeight * 1.5f + yPos -= rowHeight * 1.2f } } } + + if (options.showCustomText && options.customText.isNotEmpty()) { + val customScale = 0.5f + (options.customTextSize / 100f) + val customPaint = Paint(paint).apply { + textSize = baseSize * customScale + typeface = Typeface.DEFAULT + } + + val textBounds = Rect() + customPaint.getTextBounds(options.customText, 0, options.customText.length, textBounds) + + if (options.showExif) { + yPos -= (customPaint.textSize * 0.5f) + } + + val xPos = if (options.leftAlignOverlay) margin else (bitmap.width - margin - textBounds.width()) + + // Draw Custom Text + canvas.drawText(options.customText, xPos, yPos, customPaint) + + yPos -= customPaint.textSize * 1.2f + } + if (options.showDeviceBrand) { val brandString = buildBrandString(exifData) val brandPaint = Paint(paint).apply { @@ -209,12 +235,13 @@ class WatermarkEngine( textSize = (baseFrameHeight * 0.3f) * brandScale typeface = Typeface.DEFAULT_BOLD } + val exifPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = secondaryTextColor textSize = (baseFrameHeight * 0.2f) * dataScale } - val margin = bitmap.width * 0.05f + val margin = bitmap.width * (options.padding / 1000f) // 0 to 10% val maxAvailableWidth = if (options.showDeviceBrand) { (bitmap.width - margin * 2) * 0.6f @@ -233,14 +260,32 @@ class WatermarkEngine( } } - // Dynamic Height Calculation - val requiredHeight = max( - brandPaint.textSize * 2.5f, - totalExifHeight + (exifPaint.textSize * 2f) - ).roundToInt() - - val finalFrameHeight = max(baseFrameHeight, requiredHeight) + // Dynamic Height Calculation + var leftSideHeight = 0f + if (options.showDeviceBrand) { + leftSideHeight += brandPaint.textSize + } + if (options.showCustomText && options.customText.isNotEmpty()) { + val customTextPaint = Paint(brandPaint).apply { + val customScale = 0.5f + (options.customTextSize / 100f) + textSize = (baseFrameHeight * 0.3f) * customScale + typeface = Typeface.DEFAULT + } + if (options.showDeviceBrand) { + leftSideHeight += (baseFrameHeight * 0.1f) + } + leftSideHeight += customTextPaint.textSize + } + val contentHeightLeft = leftSideHeight + val contentHeightRight = totalExifHeight + + val minHeight = max(brandPaint.textSize, exifPaint.textSize) * 2f + + val calculatedHeight = max(contentHeightLeft, contentHeightRight) + (margin * 2) + + val finalFrameHeight = max(minHeight.roundToInt(), calculatedHeight.roundToInt()) + val newHeight = bitmap.height + finalFrameHeight val finalBitmap = Bitmap.createBitmap(bitmap.width, newHeight, Bitmap.Config.ARGB_8888) @@ -284,12 +329,39 @@ class WatermarkEngine( ) { - // Brand on Left + // Brand & Custom Text on Left + var currentLeftY = centerY + + var totalLeftHeight = 0f + val customScale = 0.5f + (options.customTextSize / 100f) + val customPaint = Paint(brandPaint).apply { + textSize = (brandPaint.textSize / (0.5f + (options.brandTextSize / 100f))) * customScale + + textSize = brandPaint.textSize * (customScale / (0.5f + (options.brandTextSize / 100f))) + typeface = Typeface.DEFAULT + } + + if (options.showDeviceBrand) totalLeftHeight += brandPaint.textSize + if (options.showCustomText && options.customText.isNotEmpty()) { + if (options.showDeviceBrand) totalLeftHeight += (brandPaint.textSize * 0.2f) + totalLeftHeight += customPaint.textSize + } + + currentLeftY = centerY - (totalLeftHeight / 2f) + (brandPaint.textSize / 1.5f) + + currentLeftY = centerY - (totalLeftHeight / 2f) + brandPaint.textSize + if (options.showDeviceBrand) { val brandString = buildBrandString(exifData) - // Vertical center: centerY + half text height (as baseline) - val brandY = centerY + (brandPaint.textSize / 3f) - canvas.drawText(brandString, margin, brandY, brandPaint) + canvas.drawText(brandString, margin, currentLeftY, brandPaint) + currentLeftY += (brandPaint.textSize * 0.2f) + customPaint.textSize + } else if (options.showCustomText && options.customText.isNotEmpty()) { + currentLeftY = centerY - (totalLeftHeight / 2f) + customPaint.textSize + } + + if (options.showCustomText && options.customText.isNotEmpty()) { + + canvas.drawText(options.customText, margin, currentLeftY, customPaint) } // Exif on Right diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt index 6293e993..c40c7699 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt @@ -27,6 +27,10 @@ class WatermarkRepository( private val PREF_LEFT_ALIGN = booleanPreferencesKey("left_align") private val PREF_BRAND_TEXT_SIZE = androidx.datastore.preferences.core.intPreferencesKey("brand_text_size") private val PREF_DATA_TEXT_SIZE = androidx.datastore.preferences.core.intPreferencesKey("data_text_size") + private val PREF_SHOW_CUSTOM_TEXT = booleanPreferencesKey("show_custom_text") + private val PREF_CUSTOM_TEXT = stringPreferencesKey("custom_text") + private val PREF_CUSTOM_TEXT_SIZE = androidx.datastore.preferences.core.intPreferencesKey("custom_text_size") + private val PREF_PADDING = androidx.datastore.preferences.core.intPreferencesKey("padding") val watermarkOptions: Flow = context.dataStore.data .map { preferences -> @@ -50,7 +54,11 @@ class WatermarkRepository( moveToTop = preferences[PREF_MOVE_TO_TOP] ?: false, leftAlignOverlay = preferences[PREF_LEFT_ALIGN] ?: false, brandTextSize = preferences[PREF_BRAND_TEXT_SIZE] ?: 50, - dataTextSize = preferences[PREF_DATA_TEXT_SIZE] ?: 50 + dataTextSize = preferences[PREF_DATA_TEXT_SIZE] ?: 50, + showCustomText = preferences[PREF_SHOW_CUSTOM_TEXT] ?: false, + customText = preferences[PREF_CUSTOM_TEXT] ?: "", + customTextSize = preferences[PREF_CUSTOM_TEXT_SIZE] ?: 50, + padding = preferences[PREF_PADDING] ?: 50 ) } @@ -101,4 +109,16 @@ class WatermarkRepository( suspend fun updateDataTextSize(size: Int) { context.dataStore.edit { it[PREF_DATA_TEXT_SIZE] = size } } + + suspend fun updateCustomTextSettings(show: Boolean, text: String, size: Int) { + context.dataStore.edit { + it[PREF_SHOW_CUSTOM_TEXT] = show + it[PREF_CUSTOM_TEXT] = text + it[PREF_CUSTOM_TEXT_SIZE] = size + } + } + + suspend fun updatePadding(padding: Int) { + context.dataStore.edit { it[PREF_PADDING] = padding } + } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt index fedbce4f..16dbe2bf 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button @@ -66,6 +67,7 @@ fun WatermarkScreen( val context = LocalContext.current val view = androidx.compose.ui.platform.LocalView.current // For haptics var showExifSheet by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(false) } + var showCustomTextSheet by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(false) } val options by viewModel.options.collectAsState() val previewState by viewModel.previewUiState.collectAsState() @@ -421,6 +423,57 @@ fun WatermarkScreen( isChecked = options.showDeviceBrand, onCheckedChange = { viewModel.setShowBrand(it) } ) + + // Custom Text Entry + Row( + modifier = Modifier + .fillMaxWidth() + .background( + color = MaterialTheme.colorScheme.surfaceBright, + shape = RoundedCornerShape(MaterialTheme.shapes.extraSmall.bottomEnd) + ) + .heightIn(min = 56.dp) + .clickable { + com.sameerasw.essentials.utils.HapticUtil.performUIHaptic(view) + showCustomTextSheet = true + } + .padding(12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Spacer(modifier = Modifier.size(2.dp)) + Icon( + painter = androidx.compose.ui.res.painterResource(id = R.drawable.rounded_edit_note_24), + contentDescription = stringResource(R.string.watermark_custom_text), + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.primary + ) + Spacer(modifier = Modifier.size(2.dp)) + + Text( + text = stringResource(R.string.watermark_custom_text), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.weight(1f) + ) + + if (options.showCustomText && options.customText.isNotEmpty()) { + Text( + text = options.customText, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis, + modifier = Modifier.widthIn(max = 100.dp) + ) + } + + Icon( + painter = androidx.compose.ui.res.painterResource(id = R.drawable.rounded_chevron_right_24), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } // Show EXIF Settings (Custom Row with Chevron) Row( @@ -496,6 +549,22 @@ fun WatermarkScreen( valueFormatter = { "${it.toInt()}%" } ) } + + // Spacing Slider + var paddingValue by androidx.compose.runtime.remember(options.padding) { androidx.compose.runtime.mutableFloatStateOf(options.padding.toFloat()) } + + ConfigSliderItem( + title = stringResource(R.string.watermark_spacing), + value = paddingValue, + onValueChange = { + paddingValue = it + com.sameerasw.essentials.utils.HapticUtil.performSliderHaptic(view) + }, + onValueChangeFinished = { viewModel.setPadding(paddingValue.toInt()) }, + valueRange = 0f..100f, + increment = 5f, + valueFormatter = { "${it.toInt()}%" } + ) } // Bottom spacing for scrolling @@ -580,5 +649,110 @@ fun WatermarkScreen( } } } + + if (showCustomTextSheet) { + val view = androidx.compose.ui.platform.LocalView.current + + // Local state for draft editing + var isEnabled by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(options.showCustomText) } + var draftText by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(options.customText) } + var draftSize by androidx.compose.runtime.remember { androidx.compose.runtime.mutableFloatStateOf(options.customTextSize.toFloat()) } + + androidx.compose.material3.ModalBottomSheet( + onDismissRequest = { showCustomTextSheet = false }, + containerColor = MaterialTheme.colorScheme.surfaceContainer + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + .padding(bottom = 32.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = stringResource(R.string.watermark_custom_text_settings), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(bottom = 8.dp) + ) + + RoundedCardContainer { + // Master Toggle + com.sameerasw.essentials.ui.components.cards.IconToggleItem( + iconRes = R.drawable.rounded_edit_note_24, + title = stringResource(R.string.watermark_custom_text), + isChecked = isEnabled, + onCheckedChange = { isEnabled = it } + ) + } + + if (isEnabled) { + RoundedCardContainer { + Column(modifier = Modifier.padding(16.dp)) { + // Text Input + androidx.compose.material3.OutlinedTextField( + value = draftText, + onValueChange = { draftText = it }, + label = { Text(stringResource(R.string.watermark_custom_text)) }, + placeholder = { Text(stringResource(R.string.watermark_custom_text_hint)) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true + ) + + Spacer(modifier = Modifier.height(16.dp)) + + // Size Slider + val density = androidx.compose.ui.platform.LocalDensity.current + Text( + text = stringResource(R.string.watermark_text_size_custom), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onSurface + ) + androidx.compose.material3.Slider( + value = draftSize, + onValueChange = { + draftSize = it + performSliderHaptic(view) + }, + valueRange = 0f..100f + ) + Text( + text = "${draftSize.toInt()}%", + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.align(Alignment.End), + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } + + // Buttons + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + OutlinedButton( + onClick = { + performUIHaptic(view) + showCustomTextSheet = false + }, + modifier = Modifier.weight(1f) + ) { + Text(stringResource(R.string.action_cancel)) + } + + Button( + onClick = { + performUIHaptic(view) + viewModel.setCustomTextSettings(isEnabled, draftText, draftSize.toInt()) + showCustomTextSheet = false + }, + modifier = Modifier.weight(1f) + ) { + Text(stringResource(R.string.action_save_changes)) + } + } + } + } + } } } diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt index 2025ba04..ddcd15fd 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt @@ -207,6 +207,20 @@ class WatermarkViewModel( } } + fun setCustomTextSettings(show: Boolean, text: String, size: Int) { + viewModelScope.launch { + watermarkRepository.updateCustomTextSettings(show, text, size) + previewSourceBitmap?.let { updatePreview() } + } + } + + fun setPadding(padding: Int) { + viewModelScope.launch { + watermarkRepository.updatePadding(padding) + previewSourceBitmap?.let { updatePreview() } + } + } + fun saveImage(uri: Uri) { viewModelScope.launch { _uiState.value = WatermarkUiState.Processing diff --git a/app/src/main/res/drawable/rounded_edit_note_24.xml b/app/src/main/res/drawable/rounded_edit_note_24.xml new file mode 100644 index 00000000..0870396a --- /dev/null +++ b/app/src/main/res/drawable/rounded_edit_note_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 52a4dae9..0366b0de 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -346,6 +346,12 @@ Align Left Brand Size Data Size + Text Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Save Changes Widget Haptic feedback From c60f69ed1bcbbc2011e87637ccfa9b2e0df47481 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 17:36:51 +0530 Subject: [PATCH 184/254] Border for watermark --- .../domain/watermark/WatermarkEngine.kt | 86 ++++++++++++++++++- .../domain/watermark/WatermarkRepository.kt | 14 ++- .../composables/watermark/WatermarkScreen.kt | 58 +++++++++++-- .../viewmodels/WatermarkViewModel.kt | 14 +++ app/src/main/res/values/strings.xml | 4 +- 5 files changed, 162 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt index 1d7f38fa..52ccab60 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt @@ -7,6 +7,9 @@ import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Rect +import android.graphics.RectF +import android.graphics.PorterDuff +import android.graphics.PorterDuffXfermode import android.graphics.Typeface import android.net.Uri import com.sameerasw.essentials.R @@ -41,7 +44,9 @@ data class WatermarkOptions( val showCustomText: Boolean = false, val customText: String = "", val customTextSize: Int = 50, - val padding: Int = 50 + val padding: Int = 50, + val borderStroke: Int = 0, + val borderCorner: Int = 0 ) class WatermarkEngine( @@ -126,10 +131,12 @@ class WatermarkEngine( suspend fun processBitmap(bitmap: Bitmap, uri: Uri, options: WatermarkOptions): Bitmap = withContext(Dispatchers.Default) { val exifData = metadataProvider.extractExif(uri) - when (options.style) { + val result = when (options.style) { WatermarkStyle.OVERLAY -> drawOverlay(bitmap, exifData, options) WatermarkStyle.FRAME -> drawFrame(bitmap, exifData, options) } + + applyBorder(result, options) } private fun drawOverlay(bitmap: Bitmap, exifData: ExifData, options: WatermarkOptions): Bitmap { @@ -294,10 +301,28 @@ class WatermarkEngine( // Draw background canvas.drawColor(bgColor) + // Create rounded version of source bitmap if needed + val sourceToDraw = if (options.borderCorner > 0) { + val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888) + val srcCanvas = Canvas(output) + val srcPaint = Paint(Paint.ANTI_ALIAS_FLAG) + val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat()) + + val minDim = kotlin.math.min(bitmap.width, bitmap.height) + val radius = minDim * (options.borderCorner / 1000f) + + srcCanvas.drawRoundRect(rect, radius, radius, srcPaint) + srcPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) + srcCanvas.drawBitmap(bitmap, 0f, 0f, srcPaint) + output + } else { + bitmap + } + // Draw Image and Text if (options.moveToTop) { // Draw Image shifted down by frameHeight - canvas.drawBitmap(bitmap, 0f, finalFrameHeight.toFloat(), null) + canvas.drawBitmap(sourceToDraw, 0f, finalFrameHeight.toFloat(), null) // Draw Text in "Forehead" val centerY = finalFrameHeight / 2f @@ -308,7 +333,7 @@ class WatermarkEngine( } else { // Draw Image at 0,0 - canvas.drawBitmap(bitmap, 0f, 0f, null) + canvas.drawBitmap(sourceToDraw, 0f, 0f, null) // Draw Text in "Chin" val centerY = bitmap.height + (finalFrameHeight / 2f) @@ -317,6 +342,10 @@ class WatermarkEngine( brandPaint, exifPaint, exifRows, bitmap.width ) } + + if (sourceToDraw != bitmap) { + sourceToDraw.recycle() + } return finalBitmap } @@ -592,4 +621,53 @@ class WatermarkEngine( raw } } + + private fun applyBorder(bitmap: Bitmap, options: WatermarkOptions): Bitmap { + if (options.borderStroke == 0 && options.borderCorner == 0) return bitmap + + val roundedBitmap = if (options.borderCorner > 0 && options.style != WatermarkStyle.FRAME) { + val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(output) + val paint = Paint(Paint.ANTI_ALIAS_FLAG) + val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat()) + + // Mapping: 0-100slider -> 0-10% of min dimension + val minDim = kotlin.math.min(bitmap.width, bitmap.height) + val radius = minDim * (options.borderCorner / 1000f) // Max 10% + + canvas.drawRoundRect(rect, radius, radius, paint) + paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) + canvas.drawBitmap(bitmap, 0f, 0f, paint) + + if (bitmap != output) bitmap.recycle() + output + } else { + bitmap + } + + // Border Stroke (Expand Canvas) + val finalBitmap = if (options.borderStroke > 0) { + val strokeWidth = (bitmap.width * (options.borderStroke / 1000f)).toInt() + + val newWidth = roundedBitmap.width + (strokeWidth * 2) + val newHeight = roundedBitmap.height + (strokeWidth * 2) + + val output = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888) + val canvas = Canvas(output) + + val useDark = options.useDarkTheme + val bgColor = if (useDark) Color.BLACK else Color.WHITE + + canvas.drawColor(bgColor) + + canvas.drawBitmap(roundedBitmap, strokeWidth.toFloat(), strokeWidth.toFloat(), null) + + if (roundedBitmap != output) roundedBitmap.recycle() + output + } else { + roundedBitmap + } + + return finalBitmap + } } diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt index c40c7699..190aa3ee 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt @@ -31,6 +31,8 @@ class WatermarkRepository( private val PREF_CUSTOM_TEXT = stringPreferencesKey("custom_text") private val PREF_CUSTOM_TEXT_SIZE = androidx.datastore.preferences.core.intPreferencesKey("custom_text_size") private val PREF_PADDING = androidx.datastore.preferences.core.intPreferencesKey("padding") + private val PREF_BORDER_STROKE = androidx.datastore.preferences.core.intPreferencesKey("border_stroke") + private val PREF_BORDER_CORNER = androidx.datastore.preferences.core.intPreferencesKey("border_corner") val watermarkOptions: Flow = context.dataStore.data .map { preferences -> @@ -58,7 +60,9 @@ class WatermarkRepository( showCustomText = preferences[PREF_SHOW_CUSTOM_TEXT] ?: false, customText = preferences[PREF_CUSTOM_TEXT] ?: "", customTextSize = preferences[PREF_CUSTOM_TEXT_SIZE] ?: 50, - padding = preferences[PREF_PADDING] ?: 50 + padding = preferences[PREF_PADDING] ?: 50, + borderStroke = preferences[PREF_BORDER_STROKE] ?: 0, + borderCorner = preferences[PREF_BORDER_CORNER] ?: 0 ) } @@ -121,4 +125,12 @@ class WatermarkRepository( suspend fun updatePadding(padding: Int) { context.dataStore.edit { it[PREF_PADDING] = padding } } + + suspend fun updateBorderStroke(stroke: Int) { + context.dataStore.edit { it[PREF_BORDER_STROKE] = stroke } + } + + suspend fun updateBorderCorner(corner: Int) { + context.dataStore.edit { it[PREF_BORDER_CORNER] = corner } + } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt index 16dbe2bf..1339f5c8 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt @@ -218,7 +218,7 @@ fun WatermarkScreen( val screenHeightDp = configuration.screenHeightDp.dp val maxPreviewHeightDp = screenHeightDp * 0.6f - val minPreviewHeightDp = screenHeightDp * 0.4f + val minPreviewHeightDp = screenHeightDp * 0.3f val maxPx = with(density) { maxPreviewHeightDp.toPx() } val minPx = with(density) { minPreviewHeightDp.toPx() } @@ -271,8 +271,8 @@ fun WatermarkScreen( .fillMaxWidth() .height(with(density) { previewHeightPx.toDp() }) .padding(16.dp) - .clip(RoundedCornerShape(24.dp)) - .background(MaterialTheme.colorScheme.surfaceContainerHigh) + .clip(if (initialUri == null) RoundedCornerShape(24.dp) else androidx.compose.ui.graphics.RectangleShape) + .background(if (initialUri == null) MaterialTheme.colorScheme.surfaceContainerHigh else androidx.compose.ui.graphics.Color.Transparent) .clickable { performUIHaptic(view) if (initialUri == null) { @@ -434,7 +434,7 @@ fun WatermarkScreen( ) .heightIn(min = 56.dp) .clickable { - com.sameerasw.essentials.utils.HapticUtil.performUIHaptic(view) + performUIHaptic(view) showCustomTextSheet = true } .padding(12.dp), @@ -485,7 +485,7 @@ fun WatermarkScreen( ) .heightIn(min = 56.dp) // Match standard item height .clickable { - com.sameerasw.essentials.utils.HapticUtil.performUIHaptic(view) + performUIHaptic(view) showExifSheet = true } .padding(12.dp), @@ -524,7 +524,7 @@ fun WatermarkScreen( value = brandSize, onValueChange = { brandSize = it - com.sameerasw.essentials.utils.HapticUtil.performSliderHaptic(view) + performSliderHaptic(view) }, onValueChangeFinished = { viewModel.setBrandTextSize(brandSize.toInt()) }, valueRange = 0f..100f, @@ -541,7 +541,7 @@ fun WatermarkScreen( value = dataSize, onValueChange = { dataSize = it - com.sameerasw.essentials.utils.HapticUtil.performSliderHaptic(view) + performSliderHaptic(view) }, onValueChangeFinished = { viewModel.setDataTextSize(dataSize.toInt()) }, valueRange = 0f..100f, @@ -558,7 +558,7 @@ fun WatermarkScreen( value = paddingValue, onValueChange = { paddingValue = it - com.sameerasw.essentials.utils.HapticUtil.performSliderHaptic(view) + performSliderHaptic(view) }, onValueChangeFinished = { viewModel.setPadding(paddingValue.toInt()) }, valueRange = 0f..100f, @@ -567,6 +567,48 @@ fun WatermarkScreen( ) } + // Border Section + Text( + text = "Border", + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 8.dp), + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + RoundedCardContainer( + modifier = Modifier.fillMaxWidth() + ) { + var strokeValue by androidx.compose.runtime.remember(options.borderStroke) { androidx.compose.runtime.mutableFloatStateOf(options.borderStroke.toFloat()) } + + ConfigSliderItem( + title = stringResource(R.string.watermark_border_width), + value = strokeValue, + onValueChange = { + strokeValue = it + performSliderHaptic(view) + }, + onValueChangeFinished = { viewModel.setBorderStroke(strokeValue.toInt()) }, + valueRange = 0f..100f, + increment = 5f, + valueFormatter = { "${it.toInt()}%" } + ) + + var cornerValue by androidx.compose.runtime.remember(options.borderCorner) { androidx.compose.runtime.mutableFloatStateOf(options.borderCorner.toFloat()) } + + ConfigSliderItem( + title = stringResource(R.string.watermark_border_corners), + value = cornerValue, + onValueChange = { + cornerValue = it + performSliderHaptic(view) + }, + onValueChangeFinished = { viewModel.setBorderCorner(cornerValue.toInt()) }, + valueRange = 0f..100f, + increment = 5f, + valueFormatter = { "${it.toInt()}%" } + ) + } + // Bottom spacing for scrolling Spacer(Modifier.height(24.dp)) } diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt index ddcd15fd..fe6f2131 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt @@ -221,6 +221,20 @@ class WatermarkViewModel( } } + fun setBorderStroke(stroke: Int) { + viewModelScope.launch { + watermarkRepository.updateBorderStroke(stroke) + previewSourceBitmap?.let { updatePreview() } + } + } + + fun setBorderCorner(corner: Int) { + viewModelScope.launch { + watermarkRepository.updateBorderCorner(corner) + previewSourceBitmap?.let { updatePreview() } + } + } + fun saveImage(uri: Uri) { viewModelScope.launch { _uiState.value = WatermarkUiState.Processing diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0366b0de..4e46b1f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -324,7 +324,7 @@ Secure apps with biometrics Freeze Disable rarely used apps - Watermarks + Watermark Add EXIF data and logos to photos Style Overlay @@ -351,6 +351,8 @@ Enter your text... Custom Text Settings Spacing + Border Width + Round Corners Save Changes From f2ad74d2e34c789612819b114376efd8dea8a275 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 17:44:59 +0530 Subject: [PATCH 185/254] Updated font size section in watermark layout --- .../domain/watermark/WatermarkRepository.kt | 4 + .../composables/watermark/WatermarkScreen.kt | 128 ++++++++++-------- .../viewmodels/WatermarkViewModel.kt | 7 + app/src/main/res/values/strings.xml | 1 + 4 files changed, 82 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt index 190aa3ee..c509dc13 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt @@ -126,6 +126,10 @@ class WatermarkRepository( context.dataStore.edit { it[PREF_PADDING] = padding } } + suspend fun updateCustomTextSize(size: Int) { + context.dataStore.edit { it[PREF_CUSTOM_TEXT_SIZE] = size } + } + suspend fun updateBorderStroke(stroke: Int) { context.dataStore.edit { it[PREF_BORDER_STROKE] = stroke } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt index 1339f5c8..701d2ebc 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt @@ -515,40 +515,7 @@ fun WatermarkScreen( ) } - // Text Size Sliders - if (options.showDeviceBrand) { - var brandSize by androidx.compose.runtime.remember(options.brandTextSize) { androidx.compose.runtime.mutableFloatStateOf(options.brandTextSize.toFloat()) } - - ConfigSliderItem( - title = stringResource(R.string.watermark_text_size_brand), - value = brandSize, - onValueChange = { - brandSize = it - performSliderHaptic(view) - }, - onValueChangeFinished = { viewModel.setBrandTextSize(brandSize.toInt()) }, - valueRange = 0f..100f, - increment = 5f, - valueFormatter = { "${it.toInt()}%" } - ) - } - if (options.showExif) { - var dataSize by androidx.compose.runtime.remember(options.dataTextSize) { androidx.compose.runtime.mutableFloatStateOf(options.dataTextSize.toFloat()) } - - ConfigSliderItem( - title = stringResource(R.string.watermark_text_size_data), - value = dataSize, - onValueChange = { - dataSize = it - performSliderHaptic(view) - }, - onValueChangeFinished = { viewModel.setDataTextSize(dataSize.toInt()) }, - valueRange = 0f..100f, - increment = 5f, - valueFormatter = { "${it.toInt()}%" } - ) - } // Spacing Slider var paddingValue by androidx.compose.runtime.remember(options.padding) { androidx.compose.runtime.mutableFloatStateOf(options.padding.toFloat()) } @@ -567,6 +534,74 @@ fun WatermarkScreen( ) } + + // Font Size Section + val showFontSection = options.showDeviceBrand || options.showExif || options.showCustomText + + if (showFontSection) { + Text( + text = stringResource(R.string.watermark_font_options), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 8.dp), + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + RoundedCardContainer( + modifier = Modifier.fillMaxWidth() + ) { + if (options.showDeviceBrand) { + var brandSize by androidx.compose.runtime.remember(options.brandTextSize) { androidx.compose.runtime.mutableFloatStateOf(options.brandTextSize.toFloat()) } + + ConfigSliderItem( + title = stringResource(R.string.watermark_text_size_brand), + value = brandSize, + onValueChange = { + brandSize = it + performSliderHaptic(view) + }, + onValueChangeFinished = { viewModel.setBrandTextSize(brandSize.toInt()) }, + valueRange = 0f..100f, + increment = 5f, + valueFormatter = { "${it.toInt()}%" } + ) + } + + if (options.showExif) { + var dataSize by androidx.compose.runtime.remember(options.dataTextSize) { androidx.compose.runtime.mutableFloatStateOf(options.dataTextSize.toFloat()) } + + ConfigSliderItem( + title = stringResource(R.string.watermark_text_size_data), + value = dataSize, + onValueChange = { + dataSize = it + performSliderHaptic(view) + }, + onValueChangeFinished = { viewModel.setDataTextSize(dataSize.toInt()) }, + valueRange = 0f..100f, + increment = 5f, + valueFormatter = { "${it.toInt()}%" } + ) + } + + if (options.showCustomText) { + var customSize by androidx.compose.runtime.remember(options.customTextSize) { androidx.compose.runtime.mutableFloatStateOf(options.customTextSize.toFloat()) } + + ConfigSliderItem( + title = stringResource(R.string.watermark_text_size_custom), + value = customSize, + onValueChange = { + customSize = it + performSliderHaptic(view) + }, + onValueChangeFinished = { viewModel.setCustomTextSize(customSize.toInt()) }, + valueRange = 0f..100f, + increment = 5f, + valueFormatter = { "${it.toInt()}%" } + ) + } + } + } + // Border Section Text( text = "Border", @@ -698,7 +733,6 @@ fun WatermarkScreen( // Local state for draft editing var isEnabled by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(options.showCustomText) } var draftText by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(options.customText) } - var draftSize by androidx.compose.runtime.remember { androidx.compose.runtime.mutableFloatStateOf(options.customTextSize.toFloat()) } androidx.compose.material3.ModalBottomSheet( onDismissRequest = { showCustomTextSheet = false }, @@ -739,30 +773,8 @@ fun WatermarkScreen( modifier = Modifier.fillMaxWidth(), singleLine = true ) - + Spacer(modifier = Modifier.height(16.dp)) - - // Size Slider - val density = androidx.compose.ui.platform.LocalDensity.current - Text( - text = stringResource(R.string.watermark_text_size_custom), - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onSurface - ) - androidx.compose.material3.Slider( - value = draftSize, - onValueChange = { - draftSize = it - performSliderHaptic(view) - }, - valueRange = 0f..100f - ) - Text( - text = "${draftSize.toInt()}%", - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.align(Alignment.End), - color = MaterialTheme.colorScheme.onSurfaceVariant - ) } } } @@ -785,7 +797,7 @@ fun WatermarkScreen( Button( onClick = { performUIHaptic(view) - viewModel.setCustomTextSettings(isEnabled, draftText, draftSize.toInt()) + viewModel.setCustomTextSettings(isEnabled, draftText, options.customTextSize) // preserve size showCustomTextSheet = false }, modifier = Modifier.weight(1f) diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt index fe6f2131..5c1bc467 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt @@ -214,6 +214,13 @@ class WatermarkViewModel( } } + fun setCustomTextSize(size: Int) { + viewModelScope.launch { + watermarkRepository.updateCustomTextSize(size) + previewSourceBitmap?.let { updatePreview() } + } + } + fun setPadding(padding: Int) { viewModelScope.launch { watermarkRepository.updatePadding(padding) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4e46b1f1..53fe1466 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -347,6 +347,7 @@ Brand Size Data Size Text Size + Font Size Custom Text Enter your text... Custom Text Settings From 8a12f0c0ca4bbe784dcd6b1986e0fff2f559f990 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 18:12:59 +0530 Subject: [PATCH 186/254] Material You color for watermark --- .../domain/watermark/WatermarkEngine.kt | 79 +++++++++++-- .../domain/watermark/WatermarkRepository.kt | 18 ++- .../composables/watermark/WatermarkScreen.kt | 104 +++++++++++++++++- .../viewmodels/WatermarkViewModel.kt | 40 ++++++- .../main/res/drawable/rounded_image_24.xml | 5 + app/src/main/res/values/strings.xml | 5 + 6 files changed, 231 insertions(+), 20 deletions(-) create mode 100644 app/src/main/res/drawable/rounded_image_24.xml diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt index 52ccab60..a158e316 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt @@ -25,6 +25,13 @@ enum class WatermarkStyle { FRAME } +enum class ColorMode { + LIGHT, + DARK, + ACCENT_LIGHT, + ACCENT_DARK +} + data class WatermarkOptions( val style: WatermarkStyle = WatermarkStyle.FRAME, val showDeviceBrand: Boolean = true, @@ -36,7 +43,8 @@ data class WatermarkOptions( val showShutterSpeed: Boolean = true, val showDate: Boolean = false, val outputQuality: Int = 100, - val useDarkTheme: Boolean = false, + val colorMode: ColorMode = ColorMode.LIGHT, + val accentColor: Int = android.graphics.Color.GRAY, val moveToTop: Boolean = false, val leftAlignOverlay: Boolean = false, val brandTextSize: Int = 50, @@ -141,12 +149,14 @@ class WatermarkEngine( private fun drawOverlay(bitmap: Bitmap, exifData: ExifData, options: WatermarkOptions): Bitmap { val canvas = Canvas(bitmap) - val useDark = options.useDarkTheme - val textColor = if (useDark) Color.BLACK else Color.WHITE - val shadowColor = if (useDark) Color.WHITE else Color.BLACK - + + // Derive Colors + val colors = deriveColors(options) + val shadowColor = colors.shadowColor + val overlayTextColor = colors.overlayTextColor + val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - color = textColor + color = overlayTextColor textSize = bitmap.width * 0.03f // 3% of width setShadowLayer(4f, 2f, 2f, shadowColor) } @@ -227,10 +237,11 @@ class WatermarkEngine( private fun drawFrame(bitmap: Bitmap, exifData: ExifData, options: WatermarkOptions): Bitmap { var baseFrameHeight = (bitmap.height * 0.10f).roundToInt() - val useDark = options.useDarkTheme - val bgColor = if (useDark) Color.BLACK else Color.WHITE - val textColor = if (useDark) Color.WHITE else Color.BLACK - val secondaryTextColor = if (useDark) Color.LTGRAY else Color.GRAY + // Derive Colors + val colors = deriveColors(options) + val bgColor = colors.bgColor + val textColor = colors.textColor + val secondaryTextColor = colors.secondaryTextColor // Setup paints early to measure // Setup paints early to measure @@ -655,8 +666,8 @@ class WatermarkEngine( val output = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888) val canvas = Canvas(output) - val useDark = options.useDarkTheme - val bgColor = if (useDark) Color.BLACK else Color.WHITE + val colors = deriveColors(options) + val bgColor = colors.bgColor canvas.drawColor(bgColor) @@ -670,4 +681,48 @@ class WatermarkEngine( return finalBitmap } + + private data class DerivedColors( + val bgColor: Int, + val textColor: Int, + val secondaryTextColor: Int, + val shadowColor: Int, + val overlayTextColor: Int + ) + + private fun deriveColors(options: WatermarkOptions): DerivedColors { + return when (options.colorMode) { + ColorMode.LIGHT -> DerivedColors(Color.WHITE, Color.BLACK, Color.GRAY, Color.BLACK, Color.WHITE) + ColorMode.DARK -> DerivedColors(Color.BLACK, Color.WHITE, Color.LTGRAY, Color.WHITE, Color.BLACK) + ColorMode.ACCENT_LIGHT -> getAccentColors(options.accentColor, false) + ColorMode.ACCENT_DARK -> getAccentColors(options.accentColor, true) + } + } + + private fun getAccentColors(baseColor: Int, dark: Boolean): DerivedColors { + val hsl = FloatArray(3) + androidx.core.graphics.ColorUtils.colorToHSL(baseColor, hsl) + + return if (dark) { + // Accent Dark: Dark BG, Light Text + hsl[2] = 0.15f // Dark BG + val bgColor = androidx.core.graphics.ColorUtils.HSLToColor(hsl) + hsl[2] = 0.9f // Light Text + val textColor = androidx.core.graphics.ColorUtils.HSLToColor(hsl) + hsl[2] = 0.7f // Secondary Text + val secondaryTextColor = androidx.core.graphics.ColorUtils.HSLToColor(hsl) + + DerivedColors(bgColor, textColor, secondaryTextColor, Color.WHITE, bgColor) + } else { + // Accent Light: Light BG, Dark Text + hsl[2] = 0.95f // Light BG + val bgColor = androidx.core.graphics.ColorUtils.HSLToColor(hsl) + hsl[2] = 0.15f // Dark Text + val textColor = androidx.core.graphics.ColorUtils.HSLToColor(hsl) + hsl[2] = 0.4f // Secondary Text + val secondaryTextColor = androidx.core.graphics.ColorUtils.HSLToColor(hsl) + + DerivedColors(bgColor, textColor, secondaryTextColor, Color.BLACK, bgColor) + } + } } diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt index c509dc13..12a8a622 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt @@ -22,7 +22,8 @@ class WatermarkRepository( private val PREF_SHOW_ISO = booleanPreferencesKey("show_iso") private val PREF_SHOW_SHUTTER = booleanPreferencesKey("show_shutter") private val PREF_SHOW_DATE = booleanPreferencesKey("show_date") - private val PREF_USE_DARK_THEME = booleanPreferencesKey("use_dark_theme") + private val PREF_COLOR_MODE = stringPreferencesKey("color_mode") + private val PREF_ACCENT_COLOR = androidx.datastore.preferences.core.intPreferencesKey("accent_color") private val PREF_MOVE_TO_TOP = booleanPreferencesKey("move_to_top") private val PREF_LEFT_ALIGN = booleanPreferencesKey("left_align") private val PREF_BRAND_TEXT_SIZE = androidx.datastore.preferences.core.intPreferencesKey("brand_text_size") @@ -52,7 +53,12 @@ class WatermarkRepository( showIso = preferences[PREF_SHOW_ISO] ?: true, showShutterSpeed = preferences[PREF_SHOW_SHUTTER] ?: true, showDate = preferences[PREF_SHOW_DATE] ?: false, - useDarkTheme = preferences[PREF_USE_DARK_THEME] ?: false, + colorMode = try { + ColorMode.valueOf(preferences[PREF_COLOR_MODE] ?: ColorMode.LIGHT.name) + } catch (e: Exception) { + ColorMode.LIGHT + }, + accentColor = preferences[PREF_ACCENT_COLOR] ?: android.graphics.Color.GRAY, moveToTop = preferences[PREF_MOVE_TO_TOP] ?: false, leftAlignOverlay = preferences[PREF_LEFT_ALIGN] ?: false, brandTextSize = preferences[PREF_BRAND_TEXT_SIZE] ?: 50, @@ -94,8 +100,12 @@ class WatermarkRepository( } } - suspend fun updateUseDarkTheme(useDark: Boolean) { - context.dataStore.edit { it[PREF_USE_DARK_THEME] = useDark } + suspend fun updateColorMode(mode: ColorMode) { + context.dataStore.edit { it[PREF_COLOR_MODE] = mode.name } + } + + suspend fun updateAccentColor(color: Int) { + context.dataStore.edit { it[PREF_ACCENT_COLOR] = color } } suspend fun updateMoveToTop(move: Boolean) { diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt index 701d2ebc..e7a7cbae 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt @@ -4,6 +4,7 @@ import android.graphics.drawable.Icon import android.net.Uri import android.widget.Toast import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -45,6 +46,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.sameerasw.essentials.R +import com.sameerasw.essentials.domain.watermark.ColorMode import com.sameerasw.essentials.domain.watermark.WatermarkStyle import com.sameerasw.essentials.ui.components.ReusableTopAppBar import com.sameerasw.essentials.ui.components.cards.IconToggleItem @@ -277,8 +279,6 @@ fun WatermarkScreen( performUIHaptic(view) if (initialUri == null) { onPickImage() - } else { - viewModel.toggleContrast() } } .padding(if (initialUri == null) 32.dp else 0.dp), @@ -602,6 +602,8 @@ fun WatermarkScreen( } } + + // Border Section Text( text = "Border", @@ -644,6 +646,49 @@ fun WatermarkScreen( ) } + // Color Section + Text( + text = stringResource(R.string.watermark_color_section), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 8.dp), + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + RoundedCardContainer( + modifier = Modifier.fillMaxWidth() + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surfaceBright) + .padding(16.dp), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + ColorModeOption( + mode = ColorMode.LIGHT, + isSelected = options.colorMode == ColorMode.LIGHT, + onClick = { viewModel.setColorMode(ColorMode.LIGHT) } + ) + ColorModeOption( + mode = ColorMode.DARK, + isSelected = options.colorMode == ColorMode.DARK, + onClick = { viewModel.setColorMode(ColorMode.DARK) } + ) + ColorModeOption( + mode = ColorMode.ACCENT_LIGHT, + accentColor = options.accentColor, + isSelected = options.colorMode == ColorMode.ACCENT_LIGHT, + onClick = { viewModel.setColorMode(ColorMode.ACCENT_LIGHT) } + ) + ColorModeOption( + mode = ColorMode.ACCENT_DARK, + accentColor = options.accentColor, + isSelected = options.colorMode == ColorMode.ACCENT_DARK, + onClick = { viewModel.setColorMode(ColorMode.ACCENT_DARK) } + ) + } + } + // Bottom spacing for scrolling Spacer(Modifier.height(24.dp)) } @@ -810,3 +855,58 @@ fun WatermarkScreen( } } } + +@Composable +private fun ColorModeOption( + mode: ColorMode, + isSelected: Boolean, + onClick: () -> Unit, + accentColor: Int? = null +) { + val view = androidx.compose.ui.platform.LocalView.current + val color = when (mode) { + ColorMode.LIGHT -> androidx.compose.ui.graphics.Color.White + ColorMode.DARK -> androidx.compose.ui.graphics.Color.Black + ColorMode.ACCENT_LIGHT, ColorMode.ACCENT_DARK -> { + // Derive a preview color for the circle + val base = accentColor ?: android.graphics.Color.GRAY + val hsl = FloatArray(3) + androidx.core.graphics.ColorUtils.colorToHSL(base, hsl) + if (mode == ColorMode.ACCENT_LIGHT) { + hsl[2] = 0.8f // Light shade + } else { + hsl[2] = 0.2f // Dark shade + } + androidx.compose.ui.graphics.Color(androidx.core.graphics.ColorUtils.HSLToColor(hsl)) + } + } + + val borderColor = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outlineVariant + val borderWidth = if (isSelected) 3.dp else 1.dp + + Box( + modifier = Modifier + .size(48.dp) + .clip(androidx.compose.foundation.shape.CircleShape) + .background(color) + .border( + width = borderWidth, + color = borderColor, + shape = androidx.compose.foundation.shape.CircleShape + ) + .clickable { + performUIHaptic(view) + onClick() + }, + contentAlignment = Alignment.Center + ) { + if (mode == ColorMode.ACCENT_LIGHT || mode == ColorMode.ACCENT_DARK) { + Icon( + painter = androidx.compose.ui.res.painterResource(id = R.drawable.rounded_image_24), + contentDescription = null, + modifier = Modifier.size(20.dp), + tint = if (mode == ColorMode.ACCENT_LIGHT) androidx.compose.ui.graphics.Color.Black else androidx.compose.ui.graphics.Color.White + ) + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt index 5c1bc467..fd3a82a1 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt @@ -8,11 +8,13 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import com.sameerasw.essentials.R +import com.sameerasw.essentials.domain.watermark.ColorMode import com.sameerasw.essentials.domain.watermark.MetadataProvider import com.sameerasw.essentials.domain.watermark.WatermarkEngine import com.sameerasw.essentials.domain.watermark.WatermarkOptions import com.sameerasw.essentials.domain.watermark.WatermarkRepository import com.sameerasw.essentials.domain.watermark.WatermarkStyle +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -102,6 +104,7 @@ class WatermarkViewModel( if (bitmap != null) { previewSourceBitmap = bitmap + extractColorFromUri(uri) updatePreview() } } catch (e: Exception) { @@ -110,6 +113,39 @@ class WatermarkViewModel( } } + private fun extractColorFromUri(uri: Uri) { + viewModelScope.launch(Dispatchers.IO) { + try { + val inputStream = context.contentResolver.openInputStream(uri) + val options = android.graphics.BitmapFactory.Options().apply { + inSampleSize = 2 + } + val bitmap = android.graphics.BitmapFactory.decodeStream(inputStream, null, options) + inputStream?.close() + + if (bitmap != null) { + androidx.palette.graphics.Palette.from(bitmap) + .maximumColorCount(32) + .clearFilters() + .generate { palette -> + val color = palette?.vibrantSwatch?.rgb + ?: palette?.mutedSwatch?.rgb + ?: palette?.lightVibrantSwatch?.rgb + ?: palette?.darkVibrantSwatch?.rgb + ?: palette?.dominantSwatch?.rgb + ?: android.graphics.Color.GRAY + + viewModelScope.launch { + watermarkRepository.updateAccentColor(color) + } + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + private fun updatePreview() { val bitmap = previewSourceBitmap ?: return val uri = currentUri ?: return @@ -173,9 +209,9 @@ class WatermarkViewModel( } } - fun toggleContrast() { + fun setColorMode(mode: ColorMode) { viewModelScope.launch { - watermarkRepository.updateUseDarkTheme(!_options.value.useDarkTheme) + watermarkRepository.updateColorMode(mode) } } diff --git a/app/src/main/res/drawable/rounded_image_24.xml b/app/src/main/res/drawable/rounded_image_24.xml new file mode 100644 index 00000000..5544c381 --- /dev/null +++ b/app/src/main/res/drawable/rounded_image_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 53fe1466..7a423e50 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -354,6 +354,11 @@ Spacing Border Width Round Corners + Color + Light + Dark + Accent Light + Accent Dark Save Changes From ec73ed7a1d0a505ef3967c09d9c2803f4fbebde0 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:32 +0530 Subject: [PATCH 187/254] New translations strings.xml (Romanian) --- app/src/main/res/values-ro/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index ccee6686..73501aef 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From bdf35857828663efe013d7107122ef03af444744 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:33 +0530 Subject: [PATCH 188/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 110f6085..98497708 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -307,6 +307,42 @@ Protéger les applis avec la biométrie Gel d\'applis Désactive les applications rarement utilisées + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Retour haptique du Widget Choisir un retour haptique pour les appuis de widget From fc79090e9717fa79c6293f5f0a2c48880fe05ba0 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:34 +0530 Subject: [PATCH 189/254] New translations strings.xml (Spanish) --- app/src/main/res/values-es/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 64b3ff0e29610d7648301cf37209f59b07a989e3 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:36 +0530 Subject: [PATCH 190/254] New translations strings.xml (Afrikaans) --- app/src/main/res/values-af/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From af5ca13fe21c6e2f7c21da1eff6b26725e7838c0 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:37 +0530 Subject: [PATCH 191/254] New translations strings.xml (Arabic) --- app/src/main/res/values-ar/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index bd831804..0125de95 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From e2426fe071558d2f08df4dd91de512ec51f946a0 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:39 +0530 Subject: [PATCH 192/254] New translations strings.xml (Catalan) --- app/src/main/res/values-ca/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From d42c00aa86a896a5595ca48f745541bcec9e8453 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:40 +0530 Subject: [PATCH 193/254] New translations strings.xml (Czech) --- app/src/main/res/values-cs/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 6a039942892a7162276baf0adb4a29e89103398b Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:41 +0530 Subject: [PATCH 194/254] New translations strings.xml (Danish) --- app/src/main/res/values-da/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From e87a446f7131d8854ac6c2d451c82bf49b954a4b Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:43 +0530 Subject: [PATCH 195/254] New translations strings.xml (German) --- app/src/main/res/values-de/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e80b6714..a8cd45d8 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -307,6 +307,42 @@ Sperre Apps mit Biometrie Einfrieren Selten verwendete Apps deaktivieren + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Haptisches Feedback bei Widgets Wähle haptisches Feedback für Widget-Tipps From c8c6d0035c91bfff8ab35c62097f896a7439ad4b Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:44 +0530 Subject: [PATCH 196/254] New translations strings.xml (Greek) --- app/src/main/res/values-el/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 60ef875c3fac2313a78346a5271b83425c603855 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:45 +0530 Subject: [PATCH 197/254] New translations strings.xml (Finnish) --- app/src/main/res/values-fi/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From ef893c372e53706147138f2a15bc8d561000512a Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:46 +0530 Subject: [PATCH 198/254] New translations strings.xml (Hebrew) --- app/src/main/res/values-he/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From bdfe70e899ad0c8c5b89412dce8e22a8d3d0efc4 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:47 +0530 Subject: [PATCH 199/254] New translations strings.xml (Hungarian) --- app/src/main/res/values-hu/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From f1dd354e2f4819a2e9b53426f5d5da86d5471595 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:49 +0530 Subject: [PATCH 200/254] New translations strings.xml (Italian) --- app/src/main/res/values-it/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 366b4ab8..8d5c5293 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -308,6 +308,42 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 40b96b9bd783287be93aa8b3bb04f3770198541a Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:50 +0530 Subject: [PATCH 201/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 31a2fa69..83712b4c 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -307,6 +307,42 @@ 生体認証でアプリを保護 フリーズ あまり使わないアプリを無効にする + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes ウィジェットの触覚フィードバック ウィジェットをタップしたときの触覚フィードバックを選択する From 4b4291ef80958fc4fe583429ce53f9262e97d5bc Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:51 +0530 Subject: [PATCH 202/254] New translations strings.xml (Korean) --- app/src/main/res/values-ko/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From fc09e7220c4dde8959ed9d6053771df9806ff469 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:53 +0530 Subject: [PATCH 203/254] New translations strings.xml (Dutch) --- app/src/main/res/values-nl/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 59bc3d05be824efb6f28b4a0a5f7d786e23478a3 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:54 +0530 Subject: [PATCH 204/254] New translations strings.xml (Norwegian) --- app/src/main/res/values-no/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 848101f3b828ddca6216bd6ceed2f973fa07f331 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:55 +0530 Subject: [PATCH 205/254] New translations strings.xml (Polish) --- app/src/main/res/values-pl/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 5c9e1612f8adf57d0890592ab4dba7ef3efc2a4f Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:56 +0530 Subject: [PATCH 206/254] New translations strings.xml (Portuguese) --- app/src/main/res/values-pt/strings.xml | 54 +++++++++++++++++++++----- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 8987af25..d143ffe9 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps @@ -386,7 +422,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -769,7 +805,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -788,14 +824,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -820,8 +856,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining + Travel alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From ca1d3ebbd08645235649e05595559bba03cfb7f0 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:58 +0530 Subject: [PATCH 207/254] New translations strings.xml (Russian) --- app/src/main/res/values-ru/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 3efdd643..9a4ce27c 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 8e8abce7b57ebfc2e4bb56d3dba31db745ca880d Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:36:59 +0530 Subject: [PATCH 208/254] New translations strings.xml (Serbian (Cyrillic)) --- app/src/main/res/values-sr/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 4de9e98abef5d36cba28d5bf08248abddc1a1917 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:00 +0530 Subject: [PATCH 209/254] New translations strings.xml (Swedish) --- app/src/main/res/values-sv/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From c2a0ca84b8e8d63d57a0e3c41e1d9ba8e79e8acb Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:01 +0530 Subject: [PATCH 210/254] New translations strings.xml (Turkish) --- app/src/main/res/values-tr/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 371c14dff3fe329a876a804855f821587166ac14 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:02 +0530 Subject: [PATCH 211/254] New translations strings.xml (Ukrainian) --- app/src/main/res/values-uk/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 7ad481ec6917c96e7351f7478d85bf677b61f128 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:04 +0530 Subject: [PATCH 212/254] New translations strings.xml (Chinese Simplified) --- app/src/main/res/values-zh/strings.xml | 54 +++++++++++++++++++++----- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 8987af25..d143ffe9 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps @@ -386,7 +422,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -769,7 +805,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -788,14 +824,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -820,8 +856,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining + Travel alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From ecb4dcad8b8b3e5e44bad7defc05bff7e57970df Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:05 +0530 Subject: [PATCH 213/254] New translations strings.xml (Chinese Traditional) --- app/src/main/res/values-zh/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index d143ffe9..2c445d46 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -422,7 +422,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -805,7 +805,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -824,14 +824,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -856,8 +856,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From 136d709ffadf285a7453f0fa0222abbdd9776510 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:06 +0530 Subject: [PATCH 214/254] New translations strings.xml (English) --- app/src/main/res/values-en/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 13f3c7de88a770471968ab91c318e104a40f071c Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:07 +0530 Subject: [PATCH 215/254] New translations strings.xml (Vietnamese) --- app/src/main/res/values-vi/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From 65960aee98d1097d9156bac64d22b9171dfedd67 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:09 +0530 Subject: [PATCH 216/254] New translations strings.xml (Portuguese, Brazilian) --- app/src/main/res/values-pt/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index d143ffe9..2c445d46 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -422,7 +422,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -805,7 +805,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -824,14 +824,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -856,8 +856,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From c874be411860fcea243c1ab8d3adf841e0ca7366 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:10 +0530 Subject: [PATCH 217/254] New translations strings.xml (Sinhala) --- app/src/main/res/values-si/strings.xml | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From c9f2ff8b61d75f683d19f070bdb5278a1d0de823 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 18:37:11 +0530 Subject: [PATCH 218/254] New translations strings.xml (Acholi) --- app/src/main/res/values-ach/strings.xml | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values-ach/strings.xml b/app/src/main/res/values-ach/strings.xml index f7e2fff0..7193a9ec 100644 --- a/app/src/main/res/values-ach/strings.xml +++ b/app/src/main/res/values-ach/strings.xml @@ -307,6 +307,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback Pick haptic feedback for widget taps From f2de52a1fbc0afa4500233e16cf09bb98bfaf23e Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 19:39:51 +0530 Subject: [PATCH 219/254] Update source file strings.xml --- app/src/main/res/values/strings.xml | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d811709c..7a423e50 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -324,6 +324,42 @@ Secure apps with biometrics Freeze Disable rarely used apps + Watermark + Add EXIF data and logos to photos + Style + Overlay + Frame + Device Brand + EXIF Data + Pick Image + Image saved to gallery + Failed to save image + Processing... + Share + EXIF Settings + Focal Length + Aperture + ISO + Shutter Speed + Date & Time + Move to Top + Align Left + Brand Size + Data Size + Text Size + Font Size + Custom Text + Enter your text... + Custom Text Settings + Spacing + Border Width + Round Corners + Color + Light + Dark + Accent Light + Accent Dark + Save Changes Widget Haptic feedback From a675e18f15a7fc707c30c3804e7eb805d19ad5e5 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 19:58:40 +0530 Subject: [PATCH 220/254] Watermark logo support --- .../domain/watermark/WatermarkEngine.kt | 130 +++++++++++----- .../domain/watermark/WatermarkRepository.kt | 23 ++- .../composables/watermark/WatermarkScreen.kt | 110 +++++++++++++ .../viewmodels/WatermarkViewModel.kt | 89 ++++++++++- app/src/main/res/drawable/apple.xml | 9 ++ app/src/main/res/drawable/cmf.xml | 145 ++++++++++++++++++ app/src/main/res/drawable/google.xml | 9 ++ app/src/main/res/drawable/moto.xml | 14 ++ app/src/main/res/drawable/nothing.xml | 11 ++ app/src/main/res/drawable/oppo.xml | 10 ++ app/src/main/res/drawable/samsung.xml | 9 ++ app/src/main/res/drawable/sony.xml | 10 ++ app/src/main/res/drawable/vivo.xml | 22 +++ app/src/main/res/drawable/xiaomi.xml | 9 ++ app/src/main/res/values/strings.xml | 3 + 15 files changed, 557 insertions(+), 46 deletions(-) create mode 100644 app/src/main/res/drawable/apple.xml create mode 100644 app/src/main/res/drawable/cmf.xml create mode 100644 app/src/main/res/drawable/google.xml create mode 100644 app/src/main/res/drawable/moto.xml create mode 100644 app/src/main/res/drawable/nothing.xml create mode 100644 app/src/main/res/drawable/oppo.xml create mode 100644 app/src/main/res/drawable/samsung.xml create mode 100644 app/src/main/res/drawable/sony.xml create mode 100644 app/src/main/res/drawable/vivo.xml create mode 100644 app/src/main/res/drawable/xiaomi.xml diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt index a158e316..6813670e 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkEngine.kt @@ -54,7 +54,10 @@ data class WatermarkOptions( val customTextSize: Int = 50, val padding: Int = 50, val borderStroke: Int = 0, - val borderCorner: Int = 0 + val borderCorner: Int = 0, + val showLogo: Boolean = false, + val logoResId: Int? = null, + val logoSize: Int = 50 ) class WatermarkEngine( @@ -161,74 +164,95 @@ class WatermarkEngine( setShadowLayer(4f, 2f, 2f, shadowColor) } - val margin = bitmap.width * (options.padding / 1000f) + val strokeSpacer = (bitmap.width * (options.borderStroke / 1000f)) + val margin = (bitmap.width * (options.padding / 1000f)) + strokeSpacer var yPos = bitmap.height - margin // Apply scaling val brandScale = 0.5f + (options.brandTextSize / 100f) val dataScale = 0.5f + (options.dataTextSize / 100f) - - // Base text size was 3% of width val baseSize = bitmap.width * 0.03f - paint.textSize = baseSize * dataScale + // Logo Calculation + var logoBitmap: Bitmap? = null + var logoWidth = 0f + var logoHeight = 0f + + if (options.showLogo && options.logoResId != null) { + val logoScale = options.logoSize / 100f + val logoSizePx = baseSize * 4f * logoScale + logoBitmap = loadVectorBitmap(context, options.logoResId, overlayTextColor) + if (logoBitmap != null) { + val ratio = logoBitmap.width.toFloat() / logoBitmap.height.toFloat() + logoHeight = logoSizePx + logoWidth = logoHeight * ratio + } + } + + // Draw EXIF if (options.showExif) { + val exifPaint = Paint(paint).apply { textSize = baseSize * dataScale } val exifItems = buildExifList(exifData, options) if (exifItems.isNotEmpty()) { - val maxWidth = bitmap.width - (margin * 2) - - // Wrap items with icons - val rows = wrapExifItems(exifItems, paint, maxWidth) + val maxWidth = bitmap.width - (margin * 2) + val rows = wrapExifItems(exifItems, exifPaint, maxWidth) val reversedRows = rows.reversed() for (row in reversedRows) { - val rowWidth = measureRowWidth(row, paint) - val rowHeight = measureRowHeight(row, paint) - - var xPos = if (options.leftAlignOverlay) margin else (bitmap.width - margin - rowWidth) - - drawExifRow(canvas, row, xPos, yPos, paint, shadowColor) - + val rowWidth = measureRowWidth(row, exifPaint) + val rowHeight = measureRowHeight(row, exifPaint) + val xPos = if (options.leftAlignOverlay) margin else (bitmap.width - margin - rowWidth) + drawExifRow(canvas, row, xPos, yPos, exifPaint, shadowColor) yPos -= rowHeight * 1.2f } } } - + // Draw Custom Text if (options.showCustomText && options.customText.isNotEmpty()) { val customScale = 0.5f + (options.customTextSize / 100f) val customPaint = Paint(paint).apply { textSize = baseSize * customScale typeface = Typeface.DEFAULT } - val textBounds = Rect() customPaint.getTextBounds(options.customText, 0, options.customText.length, textBounds) - - if (options.showExif) { - yPos -= (customPaint.textSize * 0.5f) - } - + if (options.showExif) yPos -= (customPaint.textSize * 0.5f) val xPos = if (options.leftAlignOverlay) margin else (bitmap.width - margin - textBounds.width()) - - // Draw Custom Text canvas.drawText(options.customText, xPos, yPos, customPaint) - yPos -= customPaint.textSize * 1.2f } + // Draw Brand if (options.showDeviceBrand) { val brandString = buildBrandString(exifData) val brandPaint = Paint(paint).apply { typeface = Typeface.DEFAULT_BOLD - textSize = baseSize * brandScale // Use brand scale + textSize = baseSize * brandScale } val textBounds = Rect() brandPaint.getTextBounds(brandString, 0, brandString.length, textBounds) - val xPos = if (options.leftAlignOverlay) margin else (bitmap.width - margin - textBounds.width()) - canvas.drawText(brandString, xPos, yPos, brandPaint) + yPos -= brandPaint.textSize * 1.2f + } + + // Draw the Logo at the TOP of text content + if (logoBitmap != null) { + val logoX = if (options.leftAlignOverlay) margin else (bitmap.width - margin - logoWidth) + val logoY = yPos - logoHeight - (baseSize * 0.2f) // Increased spacing + + val destRect = RectF(logoX, logoY, logoX + logoWidth, logoY + logoHeight) + + if (shadowColor != null) { + val shadowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + colorFilter = android.graphics.PorterDuffColorFilter(shadowColor, android.graphics.PorterDuff.Mode.SRC_IN) + alpha = 128 + } + val shadowRect = RectF(destRect).apply { offset(2f, 2f) } + canvas.drawBitmap(logoBitmap, null, shadowRect, shadowPaint) + } + canvas.drawBitmap(logoBitmap, null, destRect, null) } return bitmap @@ -300,7 +324,8 @@ class WatermarkEngine( val minHeight = max(brandPaint.textSize, exifPaint.textSize) * 2f - val calculatedHeight = max(contentHeightLeft, contentHeightRight) + (margin * 2) + val strokeSpacer = (bitmap.width * (options.borderStroke / 1000f)).toInt() + val calculatedHeight = max(contentHeightLeft, contentHeightRight) + (margin * 2) + strokeSpacer val finalFrameHeight = max(minHeight.roundToInt(), calculatedHeight.roundToInt()) @@ -336,7 +361,7 @@ class WatermarkEngine( canvas.drawBitmap(sourceToDraw, 0f, finalFrameHeight.toFloat(), null) // Draw Text in "Forehead" - val centerY = finalFrameHeight / 2f + val centerY = (finalFrameHeight - strokeSpacer) / 2f drawFrameContent( canvas, exifData, options, margin, centerY, brandPaint, exifPaint, exifRows, bitmap.width @@ -347,7 +372,7 @@ class WatermarkEngine( canvas.drawBitmap(sourceToDraw, 0f, 0f, null) // Draw Text in "Chin" - val centerY = bitmap.height + (finalFrameHeight / 2f) + val centerY = bitmap.height + strokeSpacer + ((finalFrameHeight - strokeSpacer) / 2f) drawFrameContent( canvas, exifData, options, margin, centerY, brandPaint, exifPaint, exifRows, bitmap.width @@ -368,15 +393,33 @@ class WatermarkEngine( exifRows: List>, canvasWidth: Int ) { + val colors = deriveColors(options) + val textColor = colors.textColor + // Logo Calculation + var logoBitmap: Bitmap? = null + var logoWidth = 0f + var logoHeight = 0f + val logoSpacing = brandPaint.textSize * 0.5f + + if (options.showLogo && options.logoResId != null) { + val logoScale = options.logoSize / 100f + val logoSizePx = brandPaint.textSize * 2.5f * logoScale + logoBitmap = loadVectorBitmap(context, options.logoResId, textColor) + if (logoBitmap != null) { + val ratio = logoBitmap.width.toFloat() / logoBitmap.height.toFloat() + logoHeight = logoSizePx + logoWidth = logoHeight * ratio + } + } + + val logoAreaWidth = if (logoBitmap != null) logoWidth + logoSpacing else 0f + // Brand & Custom Text on Left var currentLeftY = centerY - var totalLeftHeight = 0f - val customScale = 0.5f + (options.customTextSize / 100f) + val customScale = 0.5f + (options.customTextSize / 100f) val customPaint = Paint(brandPaint).apply { - textSize = (brandPaint.textSize / (0.5f + (options.brandTextSize / 100f))) * customScale - textSize = brandPaint.textSize * (customScale / (0.5f + (options.brandTextSize / 100f))) typeface = Typeface.DEFAULT } @@ -389,25 +432,30 @@ class WatermarkEngine( currentLeftY = centerY - (totalLeftHeight / 2f) + (brandPaint.textSize / 1.5f) - currentLeftY = centerY - (totalLeftHeight / 2f) + brandPaint.textSize + // Draw Logo + if (logoBitmap != null) { + val logoX = margin + val logoY = centerY - (logoHeight / 2f) + canvas.drawBitmap(logoBitmap, null, RectF(logoX, logoY, logoX + logoWidth, logoY + logoHeight), null) + } + + val textX = margin + logoAreaWidth if (options.showDeviceBrand) { val brandString = buildBrandString(exifData) - canvas.drawText(brandString, margin, currentLeftY, brandPaint) + canvas.drawText(brandString, textX, currentLeftY, brandPaint) currentLeftY += (brandPaint.textSize * 0.2f) + customPaint.textSize } else if (options.showCustomText && options.customText.isNotEmpty()) { currentLeftY = centerY - (totalLeftHeight / 2f) + customPaint.textSize } if (options.showCustomText && options.customText.isNotEmpty()) { - - canvas.drawText(options.customText, margin, currentLeftY, customPaint) + canvas.drawText(options.customText, textX, currentLeftY, customPaint) } // Exif on Right if (options.showExif && exifRows.isNotEmpty()) { val lineHeight = exifPaint.textSize * 1.5f - val centeringOffset = (exifRows.size - 1) * lineHeight / 2f var currentY = (centerY + exifPaint.textSize / 3f) - centeringOffset diff --git a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt index 12a8a622..094d8d87 100644 --- a/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/domain/watermark/WatermarkRepository.kt @@ -34,6 +34,8 @@ class WatermarkRepository( private val PREF_PADDING = androidx.datastore.preferences.core.intPreferencesKey("padding") private val PREF_BORDER_STROKE = androidx.datastore.preferences.core.intPreferencesKey("border_stroke") private val PREF_BORDER_CORNER = androidx.datastore.preferences.core.intPreferencesKey("border_corner") + private val PREF_SHOW_LOGO = booleanPreferencesKey("show_logo") + private val PREF_LOGO_SIZE = androidx.datastore.preferences.core.intPreferencesKey("logo_size") val watermarkOptions: Flow = context.dataStore.data .map { preferences -> @@ -68,7 +70,10 @@ class WatermarkRepository( customTextSize = preferences[PREF_CUSTOM_TEXT_SIZE] ?: 50, padding = preferences[PREF_PADDING] ?: 50, borderStroke = preferences[PREF_BORDER_STROKE] ?: 0, - borderCorner = preferences[PREF_BORDER_CORNER] ?: 0 + borderCorner = preferences[PREF_BORDER_CORNER] ?: 0, + showLogo = preferences[PREF_SHOW_LOGO] ?: false, + logoResId = null, + logoSize = preferences[PREF_LOGO_SIZE] ?: 50 ) } @@ -147,4 +152,20 @@ class WatermarkRepository( suspend fun updateBorderCorner(corner: Int) { context.dataStore.edit { it[PREF_BORDER_CORNER] = corner } } + + suspend fun updateLogoSettings(show: Boolean, size: Int) { + context.dataStore.edit { + it[PREF_SHOW_LOGO] = show + it[PREF_LOGO_SIZE] = size + } + } + + suspend fun updateLogoShow(show: Boolean) { + context.dataStore.edit { it[PREF_SHOW_LOGO] = show } + } + + + suspend fun updateLogoSize(size: Int) { + context.dataStore.edit { it[PREF_LOGO_SIZE] = size } + } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt index e7a7cbae..497a8f42 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -45,6 +46,9 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.carousel.HorizontalMultiBrowseCarousel import com.sameerasw.essentials.R import com.sameerasw.essentials.domain.watermark.ColorMode import com.sameerasw.essentials.domain.watermark.WatermarkStyle @@ -602,7 +606,51 @@ fun WatermarkScreen( } } + + // Logo Section + Text( + text = stringResource(R.string.watermark_logo_section), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 8.dp), + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + val logoResId by viewModel.logoResId.collectAsState() + val showLogo by viewModel.showLogo.collectAsState() + RoundedCardContainer( + modifier = Modifier.fillMaxWidth() + ) { + IconToggleItem( + iconRes = R.drawable.rounded_image_24, + title = stringResource(R.string.watermark_logo_show), + isChecked = showLogo, + onCheckedChange = { checked -> viewModel.setShowLogo(checked) } + ) + + if (showLogo) { + LogoCarouselPicker( + selectedResId = logoResId, + onLogoSelected = { resId -> viewModel.setLogoResId(resId) }, + modifier = Modifier + .fillMaxWidth() + ) + + var logoSizeValue by androidx.compose.runtime.remember(options.logoSize) { androidx.compose.runtime.mutableFloatStateOf(options.logoSize.toFloat()) } + ConfigSliderItem( + title = stringResource(R.string.watermark_logo_size), + value = logoSizeValue, + onValueChange = { + logoSizeValue = it + performSliderHaptic(view) + }, + onValueChangeFinished = { viewModel.setLogoSize(logoSizeValue.toInt()) }, + valueRange = 1f..100f, + increment = 1f, + valueFormatter = { "${it.toInt()}%" } + ) + } + } // Border Section Text( @@ -910,3 +958,65 @@ private fun ColorModeOption( } } } + +@OptIn(androidx.compose.material3.ExperimentalMaterial3Api::class) +@Composable +private fun LogoCarouselPicker( + selectedResId: Int?, + onLogoSelected: (Int) -> Unit, + modifier: Modifier = Modifier +) { + val logos = listOf( + R.drawable.apple, + R.drawable.cmf, + R.drawable.google, + R.drawable.moto, + R.drawable.nothing, + R.drawable.oppo, + R.drawable.samsung, + R.drawable.sony, + R.drawable.vivo, + R.drawable.xiaomi + ) + + val carouselState = androidx.compose.material3.carousel.rememberCarouselState { logos.size } + val view = androidx.compose.ui.platform.LocalView.current + + HorizontalMultiBrowseCarousel( + state = carouselState, + preferredItemWidth = 80.dp, + minSmallItemWidth = 5.dp, + maxSmallItemWidth = 200.dp, + itemSpacing = 2.dp, + modifier = modifier + .clip(RoundedCornerShape(4.dp)) + .background(MaterialTheme.colorScheme.surfaceBright) + .height(84.dp), + contentPadding = PaddingValues(4.dp) + ) { index -> + val resId = logos[index] + val isSelected = selectedResId == resId + val containerColor = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceContainerHigh + val contentColor = if (isSelected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface + + Box( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 1.dp) + .clip(RoundedCornerShape(8.dp)) + .background(containerColor) + .clickable { + performUIHaptic(view) + onLogoSelected(resId) + }, + contentAlignment = Alignment.Center + ) { + Icon( + painter = androidx.compose.ui.res.painterResource(id = resId), + contentDescription = null, + modifier = Modifier.size(36.dp), + tint = contentColor + ) + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt index fd3a82a1..866b03d7 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/WatermarkViewModel.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import com.sameerasw.essentials.R import com.sameerasw.essentials.domain.watermark.ColorMode +import com.sameerasw.essentials.domain.watermark.ExifData import com.sameerasw.essentials.domain.watermark.MetadataProvider import com.sameerasw.essentials.domain.watermark.WatermarkEngine import com.sameerasw.essentials.domain.watermark.WatermarkOptions @@ -33,6 +34,7 @@ sealed class WatermarkUiState { class WatermarkViewModel( private val watermarkEngine: WatermarkEngine, private val watermarkRepository: WatermarkRepository, + private val metadataProvider: MetadataProvider, private val context: Context ) : ViewModel() { @@ -44,7 +46,7 @@ class WatermarkViewModel( val metadataProvider = MetadataProvider(appContext) val engine = WatermarkEngine(appContext, metadataProvider) val repository = WatermarkRepository(appContext) - return WatermarkViewModel(engine, repository, appContext) as T + return WatermarkViewModel(engine, repository, metadataProvider, appContext) as T } } } @@ -58,6 +60,13 @@ class WatermarkViewModel( private val _options = MutableStateFlow(WatermarkOptions()) val options: StateFlow = _options.asStateFlow() + // Transient logo state (not persisted, depends on image EXIF) + private val _logoResId = MutableStateFlow(null) + val logoResId: StateFlow = _logoResId.asStateFlow() + + private val _showLogo = MutableStateFlow(false) + val showLogo: StateFlow = _showLogo.asStateFlow() + private var previewSourceBitmap: android.graphics.Bitmap? = null private var currentUri: Uri? = null @@ -70,6 +79,25 @@ class WatermarkViewModel( } } + private fun detectOemLogo(exif: ExifData): Int? { + val make = exif.make?.lowercase() ?: "" + val model = exif.model?.lowercase() ?: "" + + return when { + make.contains("apple") || model.contains("iphone") -> R.drawable.apple + make.contains("google") || model.contains("pixel") -> R.drawable.google + make.contains("samsung") -> R.drawable.samsung + make.contains("xiaomi") || make.contains("redmi") || make.contains("poco") -> R.drawable.xiaomi + make.contains("oppo") -> R.drawable.oppo + make.contains("vivo") -> R.drawable.vivo + make.contains("sony") -> R.drawable.sony + make.contains("nothing") -> R.drawable.nothing + make.contains("cmf") -> R.drawable.cmf + make.contains("motorola") || make.contains("moto") -> R.drawable.moto + else -> null + } + } + fun loadPreview(uri: Uri) { currentUri = uri viewModelScope.launch(kotlinx.coroutines.Dispatchers.IO) { @@ -104,6 +132,13 @@ class WatermarkViewModel( if (bitmap != null) { previewSourceBitmap = bitmap + + // Always derive logo from EXIF on load + val exif = metadataProvider.extractExif(uri) + val detected = detectOemLogo(exif) + _logoResId.value = detected + _showLogo.value = detected != null + extractColorFromUri(uri) updatePreview() } @@ -155,7 +190,13 @@ class WatermarkViewModel( kotlinx.coroutines.delay(600) val workingBitmap = bitmap.copy(bitmap.config ?: android.graphics.Bitmap.Config.ARGB_8888, true) - val result = watermarkEngine.processBitmap(workingBitmap, uri, _options.value) + // Merge transient logo settings with base options + val currentOptions = _options.value.copy( + logoResId = _logoResId.value, + showLogo = _showLogo.value + ) + + val result = watermarkEngine.processBitmap(workingBitmap, uri, currentOptions) val timestamp = System.currentTimeMillis() val file = File(context.cacheDir, "preview_watermark_$timestamp.jpg") @@ -278,12 +319,47 @@ class WatermarkViewModel( } } + fun setLogoSettings(show: Boolean, resId: Int?, size: Int) { + _showLogo.value = show + _logoResId.value = resId + viewModelScope.launch { + watermarkRepository.updateLogoSize(size) + watermarkRepository.updateLogoShow(show) + updatePreview() + } + } + + fun setShowLogo(show: Boolean) { + _showLogo.value = show + viewModelScope.launch { + watermarkRepository.updateLogoShow(show) + updatePreview() + } + } + + fun setLogoResId(resId: Int?) { + _logoResId.value = resId + updatePreview() + } + + fun setLogoSize(size: Int) { + viewModelScope.launch { + watermarkRepository.updateLogoSize(size) + previewSourceBitmap?.let { updatePreview() } + } + } + fun saveImage(uri: Uri) { viewModelScope.launch { _uiState.value = WatermarkUiState.Processing try { + // Merge transient logo options + val finalOptions = _options.value.copy( + logoResId = _logoResId.value, + showLogo = _showLogo.value + ) // Process image to a temporary file first - val tempFile = watermarkEngine.processImage(uri, _options.value) + val tempFile = watermarkEngine.processImage(uri, finalOptions) // Save to MediaStore (Gallery) val values = android.content.ContentValues().apply { @@ -333,8 +409,13 @@ class WatermarkViewModel( viewModelScope.launch { _uiState.value = WatermarkUiState.Processing try { + // Merge transient logo options + val finalOptions = _options.value.copy( + logoResId = _logoResId.value, + showLogo = _showLogo.value + ) // Process image to a temporary file - val tempFile = watermarkEngine.processImage(uri, _options.value) + val tempFile = watermarkEngine.processImage(uri, finalOptions) val savedUri = saveToMediaStore(tempFile) if (savedUri != null) { _uiState.value = WatermarkUiState.Idle diff --git a/app/src/main/res/drawable/apple.xml b/app/src/main/res/drawable/apple.xml new file mode 100644 index 00000000..1c20b800 --- /dev/null +++ b/app/src/main/res/drawable/apple.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/cmf.xml b/app/src/main/res/drawable/cmf.xml new file mode 100644 index 00000000..464772b2 --- /dev/null +++ b/app/src/main/res/drawable/cmf.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/google.xml b/app/src/main/res/drawable/google.xml new file mode 100644 index 00000000..c0496e45 --- /dev/null +++ b/app/src/main/res/drawable/google.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/moto.xml b/app/src/main/res/drawable/moto.xml new file mode 100644 index 00000000..e5683641 --- /dev/null +++ b/app/src/main/res/drawable/moto.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/nothing.xml b/app/src/main/res/drawable/nothing.xml new file mode 100644 index 00000000..b4153c1b --- /dev/null +++ b/app/src/main/res/drawable/nothing.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/oppo.xml b/app/src/main/res/drawable/oppo.xml new file mode 100644 index 00000000..1875ee4f --- /dev/null +++ b/app/src/main/res/drawable/oppo.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/samsung.xml b/app/src/main/res/drawable/samsung.xml new file mode 100644 index 00000000..7fc874f3 --- /dev/null +++ b/app/src/main/res/drawable/samsung.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/sony.xml b/app/src/main/res/drawable/sony.xml new file mode 100644 index 00000000..cded39ab --- /dev/null +++ b/app/src/main/res/drawable/sony.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/vivo.xml b/app/src/main/res/drawable/vivo.xml new file mode 100644 index 00000000..8d6174fe --- /dev/null +++ b/app/src/main/res/drawable/vivo.xml @@ -0,0 +1,22 @@ + + + + diff --git a/app/src/main/res/drawable/xiaomi.xml b/app/src/main/res/drawable/xiaomi.xml new file mode 100644 index 00000000..95cd9fc4 --- /dev/null +++ b/app/src/main/res/drawable/xiaomi.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7a423e50..9fe02a56 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -359,6 +359,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes From 89f3257832c0b84dad5a48f61e0ae073b2720ab3 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:19 +0530 Subject: [PATCH 221/254] New translations strings.xml (Romanian) --- app/src/main/res/values-ro/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 73501aef..fd16b8d5 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 3dc74c111777d2252e32dbb62b3cbb8969c3d57d Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:21 +0530 Subject: [PATCH 222/254] New translations strings.xml (French) --- app/src/main/res/values-fr/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 98497708..fcc11ab5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Retour haptique du Widget From 87d40486257683dbe1721595d29fd39b3935abc4 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:22 +0530 Subject: [PATCH 223/254] New translations strings.xml (Spanish) --- app/src/main/res/values-es/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 44294624aab3758396631d268839459c71f58fae Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:23 +0530 Subject: [PATCH 224/254] New translations strings.xml (Afrikaans) --- app/src/main/res/values-af/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From c414bbc2b40dcb057c9fca03105bf3b3b29278b6 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:24 +0530 Subject: [PATCH 225/254] New translations strings.xml (Arabic) --- app/src/main/res/values-ar/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 0125de95..e24f6e26 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 6be33445d8284ad4129b9dfdbb5c8d93b0f498f8 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:26 +0530 Subject: [PATCH 226/254] New translations strings.xml (Catalan) --- app/src/main/res/values-ca/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 02b8cc0780f58af0a8c58695b06289058098445c Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:27 +0530 Subject: [PATCH 227/254] New translations strings.xml (Czech) --- app/src/main/res/values-cs/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 44fd804c283f9b9ec578fb40f245ef36b5c29c53 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:29 +0530 Subject: [PATCH 228/254] New translations strings.xml (Danish) --- app/src/main/res/values-da/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 25404fca7b94989f161ed4a88235bcc3f674e35a Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:30 +0530 Subject: [PATCH 229/254] New translations strings.xml (German) --- app/src/main/res/values-de/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a8cd45d8..8cb27ea7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Haptisches Feedback bei Widgets From 8180580387c8a30e0b1e09afc2c4a3aa8d3b8240 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:31 +0530 Subject: [PATCH 230/254] New translations strings.xml (Greek) --- app/src/main/res/values-el/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 62cc881b6774e419ba885a46ee4aace9545cde00 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:33 +0530 Subject: [PATCH 231/254] New translations strings.xml (Finnish) --- app/src/main/res/values-fi/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From e7ebd5572c5ee7fb5591437d22a8da204034d1de Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:34 +0530 Subject: [PATCH 232/254] New translations strings.xml (Hebrew) --- app/src/main/res/values-he/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 671bae5897fe2782897022cb75a01cf6e363d8d2 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:35 +0530 Subject: [PATCH 233/254] New translations strings.xml (Hungarian) --- app/src/main/res/values-hu/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From d4e44898fc5bcb1d5466f92c7765d26b7edf71b6 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:36 +0530 Subject: [PATCH 234/254] New translations strings.xml (Italian) --- app/src/main/res/values-it/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 8d5c5293..78d51805 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -343,6 +343,9 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 2a280066ff506b21625f525af76e2e4be45249c9 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:37 +0530 Subject: [PATCH 235/254] New translations strings.xml (Japanese) --- app/src/main/res/values-ja/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 83712b4c..7070ac65 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes ウィジェットの触覚フィードバック From 7892b2116fb395f260a7d35f2f3a7e8586073f41 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:39 +0530 Subject: [PATCH 236/254] New translations strings.xml (Korean) --- app/src/main/res/values-ko/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 5970e20210a5dbb0a78bf7436184241f193b9563 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:40 +0530 Subject: [PATCH 237/254] New translations strings.xml (Dutch) --- app/src/main/res/values-nl/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 52166444f91fd82bcb1686eb8c15185c0476bc8d Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:41 +0530 Subject: [PATCH 238/254] New translations strings.xml (Norwegian) --- app/src/main/res/values-no/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From b8d1d4fd7c5258e96c6eff571a5a06956a7ac183 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:42 +0530 Subject: [PATCH 239/254] New translations strings.xml (Polish) --- app/src/main/res/values-pl/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 369220a489fb168deba1f0927a5428834b807781 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:43 +0530 Subject: [PATCH 240/254] New translations strings.xml (Portuguese) --- app/src/main/res/values-pt/strings.xml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 2c445d46..05b7f258 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback @@ -422,7 +425,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -805,7 +808,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -824,14 +827,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -856,8 +859,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining + Travel alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From 463f624f3fd1ec24f771c1e62fbe7ed878e5afda Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:45 +0530 Subject: [PATCH 241/254] New translations strings.xml (Russian) --- app/src/main/res/values-ru/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 9a4ce27c..ba4aba23 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 4bb963f213b46efe5cb879c6b1225d1eab1ea9ed Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:46 +0530 Subject: [PATCH 242/254] New translations strings.xml (Serbian (Cyrillic)) --- app/src/main/res/values-sr/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 6644ae43ba8f155385294ee7c51aebc1a1cff755 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:47 +0530 Subject: [PATCH 243/254] New translations strings.xml (Swedish) --- app/src/main/res/values-sv/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 1ff40294ee7f581cdb66c96c511cd21634a35e95 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:48 +0530 Subject: [PATCH 244/254] New translations strings.xml (Turkish) --- app/src/main/res/values-tr/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From cfff4316354564186a788bb45dbd85fc4bac1f23 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:50 +0530 Subject: [PATCH 245/254] New translations strings.xml (Ukrainian) --- app/src/main/res/values-uk/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 4c0ae0479cc9b3637f17536dc343f97e1508e35b Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:51 +0530 Subject: [PATCH 246/254] New translations strings.xml (Chinese Simplified) --- app/src/main/res/values-zh/strings.xml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 2c445d46..05b7f258 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku + Use Shizuku or Root or Root Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback @@ -422,7 +425,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Shizuku permission + Grant Permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -805,7 +808,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -824,14 +827,14 @@ Essentials Bug Report Send via - Location reached - Get notified when you arrive at a specific destination. + Are we there yet? + Destination nearby alerts Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open your map app, pick a location, and share it to Essentials. - Radius: %d m + Open Google Maps, pick a location, and share it to Essentials. + Alert Radius: %d m Distance to trigger the alarm Enable notification Location @@ -856,8 +859,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel Alarm active - %1$s remaining + Travel alarm active + %1$s remaining (%2$d%%) Travel Progress Shows real-time distance to destination Destination Nearby From 77a36b4caa166d889ae67bed356e9b16204b5905 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:52 +0530 Subject: [PATCH 247/254] New translations strings.xml (Chinese Traditional) --- app/src/main/res/values-zh/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 05b7f258..72aa7386 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -425,7 +425,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -808,7 +808,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -827,14 +827,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -859,8 +859,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From 409c898a17cb549e98127b6c3a3c957a4de859d0 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:54 +0530 Subject: [PATCH 248/254] New translations strings.xml (English) --- app/src/main/res/values-en/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 78443cc4c9f78cf1a0664ff17a35d6c013f31ddd Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:55 +0530 Subject: [PATCH 249/254] New translations strings.xml (Vietnamese) --- app/src/main/res/values-vi/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 113eef7fc8340841e9a0dcc8d3b084e7607f43a4 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:56 +0530 Subject: [PATCH 250/254] New translations strings.xml (Portuguese, Brazilian) --- app/src/main/res/values-pt/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 05b7f258..72aa7386 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -27,7 +27,7 @@ Another note, the biometric authentication prompt only lets you use STRONG secure class methods. Face unlock security methods in WEAK class in devices such as Pixel 7 will only be able to utilize the available other STRONG auth methods such as fingerprint or pin. Enable Button Remap - Use Shizuku or Root or Root + Use Shizuku Works with screen off (Recommended) Shizuku is not running Detected %1$s @@ -425,7 +425,7 @@ Shizuku Required for advanced commands. Install Shizuku from the Play Store. Install Shizuku - Grant Permission + Shizuku permission Required to run power-saving commands while maps is navigating. Root Access Permissions required for system actions using Root privileges. @@ -808,7 +808,7 @@ Notification lighting does not work It depends on the OEM. Some like OneUI does not seem to allow overlays above the AOD preventing the lighting effects being shown. In this case, try the ambient display as a workaround. Button remap does not work while display is off - Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku or Root\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. + Some OEMs limit the accessibility service reporting once the display is actually off but they may still work while the AOD is on. \nIn this case, you may able to use button remaps with AOD on but not with off. \n\nAs a workaround, you will need to use Shizuku permissions and turn on the \'Use Shizuku\' toggle in button remap settings which identifies and listen to hardware input events.\nThis is not guaranteed to work on all devices and needs testing.\n\nAnd even if it\'s on, Shizuku method only will be used when it\'s needed. Otherwise it will always fallback to Accessibility which also handles the blocking of the actual input during long press. Flashlight brightness does not work Only a limited number of devices got hardware and software support adjusting the flashlight intensity. \n\n\'The minimum version of Android is 13 (SDK33).\nFlashlight brightness control only supports HAL version 3.8 and higher, so among the supported devices, the latest ones (For example, Pixel 6/7, Samsung S23, etc.)\'\npolodarb/Flashlight-Tiramisu What the hell is this app? @@ -827,14 +827,14 @@ Essentials Bug Report Send via - Are we there yet? - Destination nearby alerts + Location reached + Get notified when you arrive at a specific destination. Destination Set Destination Tracking: %1$.4f, %2$.4f No destination set - Open Google Maps, pick a location, and share it to Essentials. - Alert Radius: %d m + Open your map app, pick a location, and share it to Essentials. + Radius: %d m Distance to trigger the alarm Enable notification Location @@ -859,8 +859,8 @@ Required to wake your device upon arrival. Tap to grant. %1$d m %1$.1f km - Travel alarm active - %1$s remaining (%2$d%%) + Travel Alarm active + %1$s remaining Travel Progress Shows real-time distance to destination Destination Nearby From 24961a8c89994cf1358072b7d1443ce6cb7054e2 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:57 +0530 Subject: [PATCH 251/254] New translations strings.xml (Sinhala) --- app/src/main/res/values-si/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From d6eab4c8d4d8d2806d70e03fedb88679bcea9705 Mon Sep 17 00:00:00 2001 From: Sameera Wijerathna Date: Fri, 23 Jan 2026 20:44:58 +0530 Subject: [PATCH 252/254] New translations strings.xml (Acholi) --- app/src/main/res/values-ach/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-ach/strings.xml b/app/src/main/res/values-ach/strings.xml index 7193a9ec..9f19fc6b 100644 --- a/app/src/main/res/values-ach/strings.xml +++ b/app/src/main/res/values-ach/strings.xml @@ -342,6 +342,9 @@ Dark Accent Light Accent Dark + Logo + Show Logo + Logo Size Save Changes Widget Haptic feedback From 59ff77381272902818a1abe6fa3e2c462f94a549 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 21:04:10 +0530 Subject: [PATCH 253/254] Favorite feature pinning --- .../data/repository/SettingsRepository.kt | 16 ++ .../ui/components/FavoriteCarousel.kt | 164 ++++++++++++++++++ .../ui/components/cards/FeatureCard.kt | 51 +++++- .../ui/composables/SetupFeatures.kt | 36 +++- .../essentials/viewmodels/MainViewModel.kt | 16 ++ .../drawable/rounded_bookmark_remove_24.xml | 5 + app/src/main/res/values/strings.xml | 3 + 7 files changed, 282 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/sameerasw/essentials/ui/components/FavoriteCarousel.kt create mode 100644 app/src/main/res/drawable/rounded_bookmark_remove_24.xml diff --git a/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt b/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt index b34f56e8..17459e50 100644 --- a/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt @@ -117,6 +117,8 @@ class SettingsRepository(private val context: Context) { const val KEY_SHOW_BLUETOOTH_DEVICES = "show_bluetooth_devices" const val KEY_BATTERY_WIDGET_MAX_DEVICES = "battery_widget_max_devices" const val KEY_BATTERY_WIDGET_BACKGROUND_ENABLED = "battery_widget_background_enabled" + + const val KEY_PINNED_FEATURES = "pinned_features" } // Observe changes @@ -455,4 +457,18 @@ class SettingsRepository(private val context: Context) { fun isBatteryWidgetBackgroundEnabled(): Boolean = getBoolean(KEY_BATTERY_WIDGET_BACKGROUND_ENABLED, true) fun setBatteryWidgetBackgroundEnabled(enabled: Boolean) = putBoolean(KEY_BATTERY_WIDGET_BACKGROUND_ENABLED, enabled) + + fun getPinnedFeatures(): List { + val json = prefs.getString(KEY_PINNED_FEATURES, null) + return if (json != null) { + try { + gson.fromJson(json, object : TypeToken>() {}.type) ?: emptyList() + } catch (e: Exception) { emptyList() } + } else emptyList() + } + + fun savePinnedFeatures(features: List) { + val json = gson.toJson(features) + putString(KEY_PINNED_FEATURES, json) + } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/FavoriteCarousel.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/FavoriteCarousel.kt new file mode 100644 index 00000000..2d7672cd --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/FavoriteCarousel.kt @@ -0,0 +1,164 @@ +package com.sameerasw.essentials.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.* +import androidx.compose.material3.carousel.HorizontalMultiBrowseCarousel +import androidx.compose.material3.carousel.rememberCarouselState +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.sameerasw.essentials.R +import com.sameerasw.essentials.domain.model.Feature +import com.sameerasw.essentials.domain.registry.FeatureRegistry +import com.sameerasw.essentials.ui.components.menus.SegmentedDropdownMenu +import com.sameerasw.essentials.ui.components.menus.SegmentedDropdownMenuItem +import com.sameerasw.essentials.utils.ColorUtil +import com.sameerasw.essentials.utils.HapticUtil + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun FavoriteCarousel( + pinnedKeys: List, + onFeatureClick: (Feature) -> Unit, + onFeatureLongClick: (Feature) -> Unit, + modifier: Modifier = Modifier +) { + if (pinnedKeys.isEmpty()) return + + val pinnedFeatures = remember(pinnedKeys) { + val featuresMap = FeatureRegistry.ALL_FEATURES.associateBy { it.id } + pinnedKeys.mapNotNull { featuresMap[it] } + } + + if (pinnedFeatures.isEmpty()) return + + val carouselState = rememberCarouselState { pinnedFeatures.size } + + val nestedScrollConnection = remember { + object : NestedScrollConnection { + + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource + ): Offset { + return if (available.x != 0f) { + Offset(x = available.x, y = 0f) + } else { + Offset.Zero + } + } + } + } + + Column( + modifier = modifier + .fillMaxWidth() + .nestedScroll(nestedScrollConnection) + ) { + + HorizontalMultiBrowseCarousel( + state = carouselState, + preferredItemWidth = 140.dp, + itemSpacing = 4.dp, + contentPadding = PaddingValues(horizontal = 18.dp), + modifier = Modifier + .fillMaxWidth() + .height(110.dp) + ) { index -> + val feature = pinnedFeatures[index] + val view = LocalView.current + val resolvedTitle = stringResource(id = feature.title) + var showMenu by remember { mutableStateOf(false) } + + Box( + modifier = Modifier + .fillMaxSize() + .maskClip(MaterialTheme.shapes.large) + .background(MaterialTheme.colorScheme.surfaceBright) + .pointerInput(feature) { + detectTapGestures( + onLongPress = { + HapticUtil.performVirtualKeyHaptic(view) + showMenu = true + }, + onTap = { + HapticUtil.performVirtualKeyHaptic(view) + onFeatureClick(feature) + } + ) + } + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Box( + modifier = Modifier + .size(48.dp) + .background( + color = ColorUtil.getPastelColorFor(resolvedTitle), + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(id = feature.iconRes), + contentDescription = resolvedTitle, + modifier = Modifier.size(28.dp), + tint = ColorUtil.getVibrantColorFor(resolvedTitle) + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = resolvedTitle, + style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.Center, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.fillMaxWidth() + ) + } + + SegmentedDropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + SegmentedDropdownMenuItem( + text = { Text(stringResource(R.string.action_unpin)) }, + onClick = { + showMenu = false + onFeatureLongClick(feature) + }, + leadingIcon = { + Icon( + painter = painterResource(id = R.drawable.rounded_bookmark_remove_24), + contentDescription = null + ) + } + ) + } + } + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/cards/FeatureCard.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/cards/FeatureCard.kt index 4a72748b..99a9867c 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/cards/FeatureCard.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/cards/FeatureCard.kt @@ -3,11 +3,11 @@ package com.sameerasw.essentials.ui.components.cards import androidx.annotation.StringRes import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -19,14 +19,19 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.sameerasw.essentials.R +import com.sameerasw.essentials.ui.components.menus.SegmentedDropdownMenu +import com.sameerasw.essentials.ui.components.menus.SegmentedDropdownMenuItem import com.sameerasw.essentials.utils.ColorUtil import com.sameerasw.essentials.utils.HapticUtil @@ -44,19 +49,28 @@ fun FeatureCard( onDisabledToggleClick: (() -> Unit)? = null, description: Any? = null, // Can be Int or String descriptionOverride: String? = null, // For cases where we search and prepend parent feature name - isBeta: Boolean = false + isBeta: Boolean = false, + isPinned: Boolean = false, + onPinToggle: (() -> Unit)? = null ) { val view = LocalView.current + var showMenu by remember { mutableStateOf(false) } Card( colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.surfaceBright ), shape = MaterialTheme.shapes.extraSmall, - modifier = modifier.clickable { - HapticUtil.performVirtualKeyHaptic(view) - onClick() - }) { + modifier = modifier.combinedClickable( + onClick = { + HapticUtil.performVirtualKeyHaptic(view) + onClick() + }, + onLongClick = { + HapticUtil.performVirtualKeyHaptic(view) + showMenu = true + } + )) { Box(modifier = Modifier .fillMaxWidth() .padding(16.dp)) { @@ -172,6 +186,29 @@ fun FeatureCard( } } } + + SegmentedDropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + if (onPinToggle != null) { + SegmentedDropdownMenuItem( + text = { + Text(if (isPinned) stringResource(R.string.action_unpin) else stringResource(R.string.action_pin)) + }, + onClick = { + showMenu = false + onPinToggle() + }, + leadingIcon = { + Icon( + painter = painterResource(id = if (isPinned) R.drawable.rounded_bookmark_remove_24 else R.drawable.rounded_bookmark_24), + contentDescription = null + ) + } + ) + } + } } } } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt index 041e40db..a06c6a51 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt @@ -49,6 +49,7 @@ import com.sameerasw.essentials.domain.registry.PermissionRegistry import com.sameerasw.essentials.R import com.sameerasw.essentials.ui.components.cards.FeatureCard import com.sameerasw.essentials.ui.components.containers.RoundedCardContainer +import com.sameerasw.essentials.ui.components.FavoriteCarousel import com.sameerasw.essentials.ui.components.sheets.PermissionItem import com.sameerasw.essentials.ui.components.sheets.PermissionsBottomSheet import com.sameerasw.essentials.viewmodels.MainViewModel @@ -79,6 +80,7 @@ fun SetupFeatures( viewModel.isDynamicNightLightEnabled.value viewModel.isScreenLockedSecurityEnabled.value + val pinnedFeatureKeys by viewModel.pinnedFeatureKeys val context = LocalContext.current fun buildMapsPowerSavingPermissionItems(): List { @@ -653,6 +655,28 @@ fun SetupFeatures( ) ) + if (pinnedFeatureKeys.isNotEmpty() && viewModel.searchQuery.value.isEmpty()) { + FavoriteCarousel( + pinnedKeys = pinnedFeatureKeys, + onFeatureClick = { feature -> + if (feature.category == R.string.cat_security && context is FragmentActivity) { + BiometricHelper.showBiometricPrompt( + activity = context, + title = context.getString(R.string.biometric_title_settings_format, context.getString(feature.title)), + subtitle = context.getString(R.string.biometric_subtitle_access_settings), + onSuccess = { feature.onClick(context, viewModel) } + ) + } else { + feature.onClick(context, viewModel) + } + }, + onFeatureLongClick = { feature -> + viewModel.togglePinFeature(feature.id) + }, + modifier = Modifier.padding(top = 16.dp, bottom = 16.dp) + ) + } + val searchQuery = viewModel.searchQuery.value val searchResults = viewModel.searchResults.value val isSearchingViewModel = viewModel.isSearching.value @@ -735,7 +759,11 @@ fun SetupFeatures( showToggle = false, hasMoreSettings = true, isBeta = result.isBeta, // Added isBeta - descriptionOverride = if (result.parentFeature != null) "${result.parentFeature} > ${result.description}" else result.description + descriptionOverride = if (result.parentFeature != null) "${result.parentFeature} > ${result.description}" else result.description, + isPinned = pinnedFeatureKeys.contains(result.featureKey), + onPinToggle = { + viewModel.togglePinFeature(result.featureKey) + } ) } } @@ -797,7 +825,11 @@ fun SetupFeatures( showSheet = true }, description = feature.description, - isBeta = feature.isBeta + isBeta = feature.isBeta, + isPinned = pinnedFeatureKeys.contains(feature.id), + onPinToggle = { + viewModel.togglePinFeature(feature.id) + } ) } } diff --git a/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt b/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt index 5ab3d931..8c8972ac 100644 --- a/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt +++ b/app/src/main/java/com/sameerasw/essentials/viewmodels/MainViewModel.kt @@ -52,6 +52,7 @@ class MainViewModel : ViewModel() { val isCaffeinateActive = mutableStateOf(false) val isShizukuPermissionGranted = mutableStateOf(false) val isShizukuAvailable = mutableStateOf(false) + val pinnedFeatureKeys = mutableStateOf>(emptyList()) val isNotificationListenerEnabled = mutableStateOf(false) val isMapsPowerSavingEnabled = mutableStateOf(false) val isNotificationLightingEnabled = mutableStateOf(false) @@ -205,6 +206,9 @@ class MainViewModel : ViewModel() { SettingsRepository.KEY_SNOOZE_DISCOVERED_CHANNELS, SettingsRepository.KEY_SNOOZE_BLOCKED_CHANNELS -> { appContext?.let { loadSnoozeChannels(it) } } + SettingsRepository.KEY_PINNED_FEATURES -> { + pinnedFeatureKeys.value = settingsRepository.getPinnedFeatures() + } } } @@ -353,6 +357,7 @@ class MainViewModel : ViewModel() { freezeAutoExcludedApps.value = settingsRepository.getFreezeAutoExcludedApps() isDeveloperModeEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_DEVELOPER_MODE_ENABLED) isPreReleaseCheckEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_CHECK_PRE_RELEASES_ENABLED) + pinnedFeatureKeys.value = settingsRepository.getPinnedFeatures() } fun onSearchQueryChanged(query: String, context: Context) { @@ -368,6 +373,17 @@ class MainViewModel : ViewModel() { isSearching.value = false } + fun togglePinFeature(featureId: String) { + val current = pinnedFeatureKeys.value.toMutableList() + if (current.contains(featureId)) { + current.remove(featureId) + } else { + current.add(featureId) // Append at the end to keep order + } + pinnedFeatureKeys.value = current + settingsRepository.savePinnedFeatures(current) + } + fun setAutoUpdateEnabled(enabled: Boolean, context: Context) { isAutoUpdateEnabled.value = enabled settingsRepository.putBoolean(SettingsRepository.KEY_AUTO_UPDATE_ENABLED, enabled) diff --git a/app/src/main/res/drawable/rounded_bookmark_remove_24.xml b/app/src/main/res/drawable/rounded_bookmark_remove_24.xml new file mode 100644 index 00000000..605e32ab --- /dev/null +++ b/app/src/main/res/drawable/rounded_bookmark_remove_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9fe02a56..53c40856 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -264,6 +264,9 @@ Toggle visibility for %1$s + Favorites + Pin to Favorites + Unpin from Favorites From feb5f06d5049180a346595f11aa81b611136e969 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Fri, 23 Jan 2026 21:14:11 +0530 Subject: [PATCH 254/254] Fix pitch black in watermark --- .../essentials/data/repository/SettingsRepository.kt | 11 +++++++++++ .../ui/composables/watermark/WatermarkActivity.kt | 11 +++++++++-- .../ui/composables/watermark/WatermarkScreen.kt | 4 ++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt b/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt index 17459e50..27ecbf65 100644 --- a/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt +++ b/app/src/main/java/com/sameerasw/essentials/data/repository/SettingsRepository.kt @@ -130,6 +130,17 @@ class SettingsRepository(private val context: Context) { awaitClose { prefs.unregisterOnSharedPreferenceChangeListener(listener) } } + val isPitchBlackThemeEnabled: Flow = callbackFlow { + val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> + if (key == KEY_PITCH_BLACK_THEME_ENABLED) { + trySend(getBoolean(KEY_PITCH_BLACK_THEME_ENABLED)) + } + } + trySend(getBoolean(KEY_PITCH_BLACK_THEME_ENABLED)) + prefs.registerOnSharedPreferenceChangeListener(listener) + awaitClose { prefs.unregisterOnSharedPreferenceChangeListener(listener) } + } + fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) { prefs.registerOnSharedPreferenceChangeListener(listener) } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt index b0d927de..be863a09 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkActivity.kt @@ -12,12 +12,15 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.WindowCompat import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.viewmodel.compose.viewModel import com.sameerasw.essentials.R +import com.sameerasw.essentials.data.repository.SettingsRepository import com.sameerasw.essentials.ui.theme.EssentialsTheme import com.sameerasw.essentials.viewmodels.WatermarkViewModel @@ -52,9 +55,13 @@ class WatermarkActivity : ComponentActivity() { } } + val settingsRepository = SettingsRepository(this) + setContent { - EssentialsTheme { - Surface(color = MaterialTheme.colorScheme.background) { + val isPitchBlackThemeEnabled by settingsRepository.isPitchBlackThemeEnabled.collectAsState(initial = false) + + EssentialsTheme(pitchBlackTheme = isPitchBlackThemeEnabled) { + Surface(color = MaterialTheme.colorScheme.surfaceContainer) { val context = LocalContext.current val viewModel: WatermarkViewModel = viewModel( factory = WatermarkViewModel.provideFactory(context) diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt index 497a8f42..78b41da1 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/watermark/WatermarkScreen.kt @@ -110,7 +110,7 @@ fun WatermarkScreen( onBack() }, isSmall = true, - containerColor = MaterialTheme.colorScheme.background, + containerColor = MaterialTheme.colorScheme.surfaceContainer, actions = { val pickImageButton = @Composable { // Pick Image Button @@ -217,7 +217,7 @@ fun WatermarkScreen( } ) }, - containerColor = MaterialTheme.colorScheme.background + containerColor = MaterialTheme.colorScheme.surfaceContainer ) { padding -> val density = androidx.compose.ui.platform.LocalDensity.current val configuration = androidx.compose.ui.platform.LocalConfiguration.current