96 Commits

Author SHA1 Message Date
WenHao2130
fa060dca58 manager: Refactoring the get device name logic (#152)
* manager: Import `getDeviceInfo()` from bmax/APatch

Co-authored-by: GarfieldHan <2652609017@qq.com>
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>

* manager: Use `getDeviceInfo()` instead `Build.DEVICE`

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>

---------

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
Co-authored-by: GarfieldHan <2652609017@qq.com>
2025-06-08 15:47:48 +08:00
ShirkNeko
9c7ba5b998 [skip ci]: docs: Updating the README file to add a description of support for non-GKI device builds
完整正體中文本地化

對缺失內容的完整翻譯,對現有內容參考 SukiSU-Ultra 簡體中文優化

New Crowdin updates (#140)

Co-authored-by: HSSkyBoy <HSSkyBoy@outlook.com>
Co-authored-by: cvnertnc <148134890+cvnertnc@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-07 22:23:49 +08:00
ShirkNeko
061136900a [skip ci]: New Crowdin updates (#138)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)
2025-06-03 17:16:15 +08:00
ShirkNeko
6375bf4b7c manager: Simplify state management of components for installation method selection
Remove unnecessary LKM upload logic
2025-06-03 16:57:41 +08:00
ShirkNeko
17288c086a manager: Simplify Home data initialization logic and remove unnecessary caching time 2025-06-03 16:29:41 +08:00
ShirkNeko
15747ceaa5 manager: Simplify KPM display judgment in Home messages 2025-06-03 15:48:21 +08:00
ShirkNeko
675bb20f52 manager: Modify the Show KPM function to hidden and update the related settings. 2025-06-03 15:26:03 +08:00
ShirkNeko
ec0b26a174 manager: Optimize slot selection dialog to remove unnecessary information 2025-06-03 15:15:48 +08:00
ShirkNeko
92f6f2f51e manager: Add card shadow effect control 2025-06-03 14:04:55 +08:00
5ec1cff
587e73b449 manager: persist show system app settings 2025-06-03 13:53:45 +08:00
ShirkNeko
07c9cce4b9 manager: Fixed flickering on activity refresh using a clever method.
- Add Activity lifecycle callback and method to refresh current Activity
2025-06-03 01:43:31 +08:00
ShirkNeko
1d34ea4995 Rename files and update package structure
Add tool classes related to displaying and refreshing data
2025-06-03 01:02:08 +08:00
5ec1cff
d58ec6952c throne_tracker: avoid cross fs access 2025-06-03 00:03:16 +08:00
ShirkNeko
50631aade6 Manager: Refactoring of hidden messages and display of KPM settings status management 2025-06-03 00:00:39 +08:00
ShirkNeko
6df8f6f5d4 refactor: simplify handling and add app restart utility 2025-06-02 22:47:30 +08:00
米凛MiRin
4aee26b48e manager: auto restart after Display KPM Function and Hide other info (#136) 2025-06-02 22:24:17 +08:00
ShirkNeko
3bbe415c7e Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-06-02 21:33:55 +08:00
ShirkNeko
892fa9040f docs: add troubleshooting section for KernelSU Manager uninstallation issues 2025-06-02 21:33:48 +08:00
ShirkNeko
cadc123eab [skip ci] New Crowdin updates (#135)
* New translations strings.xml (Romanian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)
2025-06-02 21:30:13 +08:00
ShirkNeko
3a27537648 [skip ci] strings: update the description of the hidden additional information 2025-06-02 21:06:08 +08:00
ShirkNeko
6fa1a5c8b8 Optimize the logic of badge display in the bottom navigation bar
Add support for settings to hide other information

README: add notice about stuck device solution (#134)

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: =?UTF-8?q?=E7=B1=B3=E5=87=9BMiRin?= <148533509+MiRinChan@users.noreply.github.com>

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-06-02 20:46:49 +08:00
ShirkNeko
b772c8ece1 manager: Refactoring data refresh management
New AppData object to optimize data fetching and state management.
2025-06-02 17:49:34 +08:00
ShirkNeko
c0e839dd8e manager: Implement module count refresh
- update count data periodically and optimize the bottom bar display
2025-06-02 17:24:34 +08:00
ShirkNeko
a6ed7befdc manager: Add count icon to bottom bar
- Remove count icon from Home
- Add put back button for more settings
2025-06-02 16:43:31 +08:00
米凛MiRin
c210b00d54 manager: accessibility optimization (#131)
manager: style optimization
2025-06-02 14:47:59 +08:00
ShirkNeko
13b5290598 [skip ci]New Crowdin updates (#133)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* New translations strings.xml (Japanese)

* New translations strings.xml (Chinese Simplified)
2025-06-02 14:34:41 +08:00
ShirkNeko
b99516da69 [skip ci]: New Crowdin updates (#130)
* Update source file strings.xml

* New translations strings.xml (Romanian)

* New translations strings.xml (Danish)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)
2025-06-02 14:03:10 +08:00
lshwjgpt25
fe8b5f2135 [skip ci]: Align superuser app spacing with module spacing (#132) 2025-06-02 13:31:41 +08:00
ShirkNeko
04e1b9bf77 manager: Update back navigation logic in FlashScreen for module flashing 2025-06-02 04:10:41 +08:00
ShirkNeko
b8aaf918fe Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-06-02 02:11:40 +08:00
ShirkNeko
54925188e8 Optimize interface element spacing and styles 2025-06-02 02:09:46 +08:00
yycgit1
3443e48ef1 manager: Add alternate app icon toggle (#129)
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
Co-authored-by: MiRinChan <148533509+MiRinChan@users.noreply.github.com>
2025-06-01 22:43:08 +08:00
ShirkNeko
53b3e84890 Optimize button styles in SuperUser 2025-06-01 22:36:23 +08:00
ShirkNeko
a5b85bfdad manager: Update theme color display in MoreSettings screen and adjust title style 2025-06-01 21:56:56 +08:00
ShirkNeko
2817583e3c Optimize icon handling for settings cards
Allow icons to be optional and remove redundant group title displays
2025-06-01 21:24:21 +08:00
ShirkNeko
8a6116b4ec manager: Update surface colors to use cardAlpha for improved theme consistency 2025-06-01 20:58:28 +08:00
cvnertnc
6a4270787a [skip ci]Manager: update values-tr/strings.xml (#126) 2025-06-01 20:29:03 +08:00
ShirkNeko
5457a4772b manager: Refactor card elevation handling and improve theme support 2025-06-01 20:17:22 +08:00
ShirkNeko
ee4c3bb03b manager: Optimize color schemes for themes and module screens
- Remove unnecessary background color settings
2025-06-01 14:04:10 +08:00
ShirkNeko
dd1d17d2cf Optimize the display logic of reboot drop down menu items 2025-06-01 02:27:55 +08:00
WenHao2130
3c353e8f88 manager: Modify the display logic of safe_mode (#125)
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-06-01 01:30:16 +08:00
ShirkNeko
d743073309 docs: Add instructions for manually integrating susfs 2025-06-01 00:20:38 +08:00
ShirkNeko
a636911612 [skip ci]: New Crowdin updates (#123)
* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-06-01 00:16:11 +08:00
ShirkNeko
7a62f91752 manager: Using SwitchItem instead of ListItem 2025-06-01 00:07:17 +08:00
米凛MiRin
b551a54c8f manager: convert bitmap image to vector by hand. document: add copyright infomation. (#121) 2025-05-31 23:30:37 +08:00
ShirkNeko
26d86aa2fe manager: Optimize Home data refresh logic 2025-05-31 23:29:34 +08:00
ShirkNeko
6ee9246650 Fixes the problem of not refreshing automatically 2025-05-31 20:59:47 +08:00
ShirkNeko
1cd96fbdbf Optimize data preloading 2025-05-31 20:40:55 +08:00
ShirkNeko
a030a026b1 Manager: optimizing Home's data caching logic 2025-05-31 20:26:23 +08:00
ShirkNeko
8bf9cd0bee manager: Add initialization to optimize loading of SuperUser and Home data 2025-05-31 19:17:43 +08:00
ShirkNeko
13b1aad4b8 manager: Optimizing Home Performance
- Reorganize Home structure using MVVM architecture pattern to separate UI and data logic
2025-05-31 17:39:24 +08:00
ShirkNeko
916d956ce2 [skip ci]: New Crowdin updates (#120)
* Update source file strings.xml

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-05-31 13:28:54 +08:00
ShirkNeko
87a7650d26 New Crowdin updates (#119)
* Update source file strings.xml

* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* Update source file strings.xml
2025-05-31 13:19:14 +08:00
ShirkNeko
3484e187da manager: Refactoring the settings interface
- Merge Web Debugging and Web X Eruda switches
2025-05-31 12:49:58 +08:00
ShirkNeko
0835f330e2 manager: Modifying the WebUI Engine Global Options 2025-05-31 04:41:16 +08:00
ShirkNeko
8064472477 manager: better handle webui engine select
- Optimize the flashback problem caused by null pointer

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-31 03:03:22 +08:00
ShirkNeko
2281012e33 manager: Fallback allows developers to override user preferences for the selected WebUI engine
- Because the WebUI can cause some problems, the automatic fetching of the
2025-05-31 02:14:25 +08:00
ShirkNeko
83eaeab1ba New Crowdin updates (#115)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-05-30 20:17:37 +08:00
ShirkNeko
6405764df3 Adjust settings and optimize theme styles 2025-05-30 20:00:33 +08:00
ShirkNeko
253276a27b Remove border styles from labels 2025-05-29 20:25:17 +08:00
ShirkNeko
855a71ac56 Adjust the Dark Mode Card Transparency setting to the default value of 1 2025-05-29 18:48:43 +08:00
ShirkNeko
96dc53977f manager: Refactoring kernel flash features and styles 2025-05-29 18:25:45 +08:00
ShirkNeko
31111e68eb [skip ci]: New translations strings.xml (Russian) (#113) 2025-05-29 15:21:14 +08:00
ShirkNeko
ac0de29872 Remove cards from Home 2025-05-29 15:00:05 +08:00
ShirkNeko
9e2b722491 manager: Adding Vacancies to WeiUI Configuration and Implementing Asynchronous Loading
- Upgrade agp version to 8.10.1
2025-05-29 14:44:24 +08:00
ShirkNeko
59627e6fe2 manager: update Crowdin workflow to include resource paths 2025-05-28 13:53:58 +08:00
ShirkNeko
cd0b5fb378 New Crowdin updates (#111)
* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (German)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Russian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Kannada)

* New translations strings.xml (Spanish)

* New translations strings.xml (Danish)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Bosnian)
2025-05-28 13:47:41 +08:00
ShirkNeko
48a3c64c7c New translations strings.xml (Vietnamese) (#109) 2025-05-28 12:22:58 +08:00
米凛MiRin
62da804518 manager: ksuEngine as default WebUI engine (#110) 2025-05-28 12:22:38 +08:00
ShirkNeko
439b99cc4a manager: refactor label item in superuser list
* manager: Improvements

* manager: bump mmrl

* manager: use ktx ext Str.toUri

* manager: add "webui-engine" from config.json

This allows the developer to override the user preference of the selected WebUI engine.

Supported engines are:

- `wx` for WebUI X
- `ksu` for the KernelSU WebUI

All not named strings will default to `wx`

R.string.use_webuix_summary needs proper translations

* manager: add support for multilingual module meta

Co-authored-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-27 16:57:54 +08:00
ShirkNeko
64f0efc2c0 manager: use myUserId as fallback
Co-authored-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-27 16:33:58 +08:00
ShirkNeko
f196bf5b76 manager: Updated Kpm and version info icons 2025-05-27 16:02:36 +08:00
ShirkNeko
790968be6a manager: Change icon 2025-05-27 15:47:24 +08:00
ShirkNeko
83f0f9537f New Crowdin updates (#107)
* New translations strings.xml (French)

* New translations strings.xml (Arabic)

* New translations strings.xml (German)

* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Thai)

* New translations strings.xml (Romanian)

* New translations strings.xml (Spanish)

* New translations strings.xml (Danish)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)
2025-05-27 01:33:53 +08:00
ShirkNeko
68ebfec918 manager: Optimize the logic of displaying the Machine Architecture tab of the home status card
Fix the problem of displaying text in safe mode
2025-05-27 01:07:24 +08:00
ShirkNeko
8be4dea081 manager: Update interface card color to surfaceContainerLow to optimize visual effect. 2025-05-24 22:00:52 +08:00
Rifat Azad
cfdbba45c3 manager: make action execution screen have the same behavior as Magisk
based on pr https://github.com/tiann/KernelSU/pull/2321

* Magisk's behavior: Hide Bottom Navbar, Show close button if failed or success
and removed automatic exit when module execution success.
2025-05-24 17:03:20 +08:00
WenHao2130
d408c9f4bf manager: Modify Module page icon (#104)
Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-05-24 15:33:50 +08:00
ShirkNeko
8f4c58c4c3 [skip ci]: kernel: simplify KPM enabled check in ksu_handle_prctl 2025-05-24 15:25:54 +08:00
rsuntk
7e88e9648f kernel: guard nuke_ext4_sysfs
Rather than using depends on / select,
i just prefer this way, although, yes, it is
an ifdef hell.

Signed-off-by: rsuntk <rsuntk@yukiprjkt.my.id>
2025-05-24 15:05:44 +08:00
ShirkNeko
4516d136a4 Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-05-24 14:11:35 +08:00
ShirkNeko
1b85dfbed1 manager: Modify the text padding in the ElevatedCard
- Adding Formatting Characters

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
2025-05-24 14:11:14 +08:00
ShirkNeko
807ffb419a [skip ci] : Update source file strings.xml (#101) 2025-05-24 13:55:43 +08:00
ShirkNeko
e826f43aed Optimize KPM checking logic
- Simplify code and ensure KPM information is displayed under supported versions
2025-05-24 04:38:54 +08:00
ShirkNeko
d619f5fafc Refactoring KPM support to check KPM status using CMD_ENABLE_KPM 2025-05-24 03:28:28 +08:00
ShirkNeko
b3e2f9b7ff manager: Updated colors and styles
- Adapted from reference style

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-24 00:47:15 +08:00
ShirkNeko
99a39c6f52 New Crowdin updates (#100)
* Update source file strings.xml

* New translations strings.xml (Romanian)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Arabic)

* New translations strings.xml (Danish)

* New translations strings.xml (German)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Italian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Korean)

* New translations strings.xml (Lithuanian)

* New translations strings.xml (Dutch)

* New translations strings.xml (Polish)

* New translations strings.xml (Russian)

* New translations strings.xml (Slovenian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Persian)

* New translations strings.xml (Marathi)

* New translations strings.xml (Thai)

* New translations strings.xml (Croatian)

* New translations strings.xml (Estonian)

* New translations strings.xml (Latvian)

* New translations strings.xml (Azerbaijani)

* New translations strings.xml (Hindi)

* New translations strings.xml (Malay)

* New translations strings.xml (Filipino)

* New translations strings.xml (Chinese Traditional, Hong Kong)

* New translations strings.xml (Bosnian)

* New translations strings.xml (Kannada)

* Update source file strings.xml
2025-05-23 17:06:24 +08:00
ShirkNeko
22991e8740 Merge branch 'main' of https://github.com/SukiSU-Ultra/SukiSU-Ultra 2025-05-23 16:46:31 +08:00
ShirkNeko
7646ecb6f7 manager: Update theme color scheme, fix style
* Remove redundant strings
* Bump MMRL

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: liaowenxuan <jby13147208050@163.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-05-23 16:45:17 +08:00
ShirkNeko
204db674bb [skip ci]: New Crowdin updates (#99)
* New translations strings.xml (Spanish)

* New translations strings.xml (Portuguese, Brazilian)
2025-05-23 16:38:20 +08:00
ShirkNeko
99fe6623de manager: possible fix
- Possible fix a bug where IUserManager.getUsers(ZZZ) are not defined in the framework.jar
- Refactored WebUI X to meet the new WXInterface
- Only fetch the app from the current user and not all users

* manager: remove unused AIDL interfaces

Signed-off-by: Der_Googler <54764558+DerGoogler@users.noreply.github.com>
2025-05-23 16:31:13 +08:00
ShirkNeko
f1f78d2485 Add force refresh to get module list after installing a module 2025-05-22 18:12:13 +08:00
ShirkNeko
b2ae20b796 manager: Enhance and simplify module name capture
- Add the use of incoming module name to load the corresponding installation list when the file cannot be retrieved, using utf-8 encoding and formatting characters by default.
2025-05-20 22:23:45 +08:00
ShirkNeko
83bd4e9642 New translations strings.xml (French) (#95) 2025-05-20 19:34:08 +08:00
ShirkNeko
767349798a docs: Added a link to submit translations to the Crowdin project page in the documentation 2025-05-20 19:31:44 +08:00
Jiu
ae38f4709b [skip ci]: 删掉本地配置sdk (#94)
* feat: Update string resources for clarity and consistency; simplify build manager workflow

* 使用gki-kernel-local.yml

* build-lkm-local.yml

* 修改为谷歌源

* 修改为清华源

* 修改max-size为16G

* Updating Vietnamese strings

* kernel: kpm: add compatibility for kernel 4.14 and lower (#76)

`thread_pid` is not defined in kernel 4.14 and lower, leading to compilation issue.
To fix this, use `pids[PIDTYPE_PID].pid` for kernel versions 4.14 and lower.
Else use `thread_pid` for kernel versions 4.19 and higher.

Reference: 107717913b/tracee/tracee.bpf.c (L354)

* 1

* 1

* 1

---------

Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: KernelSUBot <bot@kernelsu.org>
Co-authored-by: sidex15 <24408329+sidex15@users.noreply.github.com>
Co-authored-by: build <123456@cnm.com>
2025-05-20 19:26:49 +08:00
127 changed files with 8792 additions and 6560 deletions

View File

@@ -150,18 +150,6 @@ jobs:
echo "${{ secrets.KEYSTORE }}" | base64 -d > key.jks
fi
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Download arm64 susfs
uses: actions/download-artifact@v4
with:
@@ -213,6 +201,8 @@ jobs:
- name: Build with Gradle
run: |
export ANDROID_HOME=/root/.android/sdk
export PATH=$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH
{
echo 'org.gradle.parallel=true'
echo 'org.gradle.vfs.watch=true'
@@ -221,6 +211,7 @@ jobs:
} >> gradle.properties
sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties
./gradlew clean assembleRelease
- name: Upload build artifact
uses: actions/upload-artifact@v4
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}

View File

@@ -3,6 +3,8 @@ name: Crowdin Action
on:
push:
branches: [ main ]
paths:
- 'manager/app/src/main/res/**'
jobs:
synchronize-with-crowdin:

View File

@@ -162,6 +162,8 @@ jobs:
if: ${{ inputs.build_lkm == true }}
working-directory: android-kernel
run: |
pip install ast-grep-cli
sudo apt-get install llvm-15 -y
ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c
sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig

View File

@@ -12,19 +12,19 @@ Android device root solution based on [KernelSU](https://github.com/tiann/Kernel
## How to add
Using main branching (non-GKI device builds are not supported)
Using main branching (non-GKI device builds are not supported) (requires manual integration of susfs)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
```
Using branches that support non-GKI devices
Using branches that support non-GKI devices (requires manual integration of susfs)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
```
## How to use integrated susfs
1. Use the susfs-dev branch directly without any patching
1. Use the susfs-dev branch directly without any patching (Support for non-GKI device builds)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
@@ -60,6 +60,8 @@ KPM templates: https://github.com/udochina/KPM-Build-Anywhere
## More links
**If you need to submit a translation for the manager go to** https://crowdin.com/project/SukiSU-Ultra
Projects compiled based on Sukisu and susfs
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
@@ -108,10 +110,17 @@ Please **all** refer to https://kernelsu.org/zh_CN/guide/installation.html
5. More customization
6. Support for KPM kernel modules
## Troubleshooting
1. Uninstalling the KernelSU Manager device is stuck. → Uninstall the application with package name com.sony.playmemories.mobile.
## License
- The file in the “kernel” directory is under [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) license.
- All other parts except the “kernel” directory are under [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html) license.
- The images of the files `ic_launcher(?!.*alt.*).*` with anime character emoticons are copyrighted by [五十根大虾仁](https://space.bilibili.com/370927), the Brand Intellectual Property in the images is owned by [明风OuO](https://space.bilibili.com/274939213), and the vectorization is done by @MiRinChan. Before using these files, in addition to complying with [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt), you also need to comply with the authorization of the two authors to use these artistic contents.
- Except for the files or directories mentioned above, all other parts are under [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html) license.
## Sponsorship list
@@ -120,8 +129,8 @@ Please **all** refer to https://kernelsu.org/zh_CN/guide/installation.html
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks
- [DARKWWEE](https://github.com/DARKWWEE) Thanks for the 100 USDT Lao
If the above list does not have your name, I will update it as soon as possible, and thanks again for your support!
- [Saksham Singla](https://github.com/TypeFlu) Website provision as well as maintenance
- [OukaroMF](https://github.com/OukaroMF) Donation of website domain name
## Contributions

View File

@@ -13,19 +13,19 @@
## 追加方法
メイン分岐の使用GKI デバイス以外のビルドはサポートされていません。)
メイン分岐の使用GKI デバイス以外のビルドはサポートされていません。) (手動によるサスフ統合が必要)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
```
GKI以外のデバイスをサポートするブランチを使用する
GKI以外のデバイスをサポートするブランチを使用する (手動によるサスフ統合が必要)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
```
## 統合された susfs の使い方
1. パッチを当てずに susfs-dev ブランチを直接使用してください
1. パッチを当てずに susfs-dev ブランチを直接使用してください (非GKIデバイスビルドをサポート)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
```
@@ -60,7 +60,7 @@ KPM テンプレートのアドレス: https://github.com/udochina/KPM-Build-Any
## その他のリンク
SukiSU と susfs をベースにコンパイルされたプロジェクトです
**監督に翻訳を提出する必要がある場合は、https://crowdin.com/project/SukiSU-Ultra
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
@@ -108,10 +108,15 @@ https://kernelsu.org/zh_CN/guide/installation.html をご参照ください。
5. その他のカスタマイズ
6. KPM カーネルモジュールに対応
## トラブルシューティング
1.KernelSU Managerのアンインストールができない。 →パッケージ名com.sony.playmemories.mobile.KernelSU Managerのアプリケーションをアンインストールする。
## ライセンス
- kernel ディレクトリのファイルは [GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.ja.html) のみライセンス下にあります。
- “kernel” ディレクトリを除くその他すべての部分は [GPL-3.0 またはそれ以降](https://www.gnu.org/licenses/gpl-3.0.html) のライセンス下にあります。
- `kernel` ディレクトリ以下のファイルは[GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)す。
- アニメキャラクターの絵文字を含むファイル `ic_launcher(?!.*alt.*).*` の画像は[五十根大虾仁](https://space.bilibili.com/370927)が著作権を所有しており、画像内のブランド知的財産権は[明风OuO](https://space.bilibili.com/274939213)が所有しています。ベクトル化は @MiRinChan が行っています。これらのファイルを使用する前に、[Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)に準拠することに加えて、これらの芸術コンテンツを使用するには、2人の著者の許可にも従う必要があります。
- 上記のファイルまたはディレクトリを除き、その他のすべての部分は[GPL-3.0以降](https://www.gnu.org/licenses/gpl-3.0.html)です。
## スポンサーシップの一覧
@@ -120,8 +125,8 @@ https://kernelsu.org/zh_CN/guide/installation.html をご参照ください。
- [wswzgdg](https://github.com/wswzgdg) このプロジェクトを支援していただき、ありがとうございます。
- [yspbwx2010](https://github.com/yspbwx2010) どうもありがとう。
- [DARKWWEE](https://github.com/DARKWWEE) ラオウ100USDTありがとう
上記の一覧にあなたの名前がない場合は、できるだけ早急に更新しますので再度ご支援をお願いします。
- [Saksham Singla](https://github.com/TypeFlu) ウェブサイトの提供およびメンテナンス
- [OukaroMF](https://github.com/OukaroMF) ウェブサイトドメイン名の寄付
## 貢献者

View File

@@ -15,11 +15,13 @@
Çekirdek kaynak kodunun kök dizininde aşağıdaki komutları çalıştırın:
Ana dalı kullanın (GKI olmayan cihazlar için desteklenmez)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
```
GKI olmayan cihazları destekleyen dalı kullanın
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
```
@@ -37,15 +39,16 @@ curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kern
- Bu bölüm [rsuntk\'nin kanca yöntemlerinden](https://github.com/rsuntk/KernelSU) alıntılanmıştır
1. **KPROBES Kancası:**
- Yüklenebilir çekirdek modülleri (LKM) için kullanılır
- GKI 2.0 çekirdeğinin varsayılan kanca yöntemi
- `CONFIG_KPROBES=y` gerektirir
- Yüklenebilir çekirdek modülleri (LKM) için kullanılır
- GKI 2.0 çekirdeğinin varsayılan kanca yöntemi
- `CONFIG_KPROBES=y` gerektirir
2. **Manuel Kanca:**
- Standart KernelSU kancası: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
- backslashxx\'nin syscall manuel kancası: https://github.com/backslashxx/KernelSU/issues/5
- GKI olmayan çekirdeğin varsayılan kanca yöntemi
- `CONFIG_KSU_MANUAL_HOOK=y` gerektirir
- Standart KernelSU kancası: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
- backslashxx\'nin syscall manuel kancası: https://github.com/backslashxx/KernelSU/issues/5
- GKI olmayan çekirdeğin varsayılan kanca yöntemi
- `CONFIG_KSU_MANUAL_HOOK=y` gerektirir
## KPM Desteği
@@ -57,6 +60,7 @@ Kaynak kodu: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
KPM şablonu: https://github.com/udochina/KPM-Build-Anywhere
> [!Note]
>
> 1. `CONFIG_KPM=y` gerektirir
> 2. GKI olmayan cihazlar ayrıca `CONFIG_KALLSYMS=y` ve `CONFIG_KALLSYMS_ALL=y` gerektirir
> 3. Bazı çekirdek `4.19` altı kaynak kodları, `4.19`dan geri taşınan başlık dosyası `set_memory.h` gerektirir
@@ -79,6 +83,7 @@ KPM şablonu: https://github.com/udochina/KPM-Build-Anywhere
## Daha Fazla Bağlantı
SukiSU ve susfs tabanlı derlenen projeler
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
@@ -89,6 +94,7 @@ SukiSU ve susfs tabanlı derlenen projeler
Lütfen **tümünü** https://kernelsu.org/zh_CN/guide/installation.html adresinden inceleyin
> [!Note]
>
> 1. Xiaomi, Redmi, Samsung gibi GKI 2.0 cihazlar için uygundur (Meizu, OnePlus, Realme ve Oppo gibi değiştirilmiş çekirdekli üreticiler hariç)
> 2. [Daha fazla bağlantı](#daha-fazla-bağlantı) bölümündeki GKI tabanlı projeleri bulun. Cihaz çekirdek sürümünü bulun. Ardından indirin ve TWRP veya çekirdek yazma aracı kullanarak AnyKernel3 soneki olan sıkıştırılmış paketi yazın
> 3. Genellikle sonek olmayan .zip sıkıştırılmış paketler sıkıştırılmamıştır, gz soneki olanlar ise Dimensity modelleri için kullanılan sıkıştırma yöntemidir
@@ -98,6 +104,7 @@ Lütfen **tümünü** https://kernelsu.org/zh_CN/guide/installation.html adresin
1. Daha fazla bağlantı bölümündeki OnePlus projesini bulun ve kendiniz doldurun, ardından bulut derleme yapın ve AnyKernel3 soneki olan sıkıştırılmış paketi yazın
> [!Note]
>
> - Çekirdek sürümü için yalnızca ilk iki haneyi doldurmanız yeterlidir, örneğin 5.10, 5.15, 6.1, 6.6
> - İşlemci kod adını kendiniz arayın, genellikle tamamen İngilizce ve sayı içermeden oluşur
> - Dal ve yapılandırma dosyasını kendiniz OnePlus çekirdek kaynak kodundan doldurun
@@ -114,9 +121,11 @@ Lütfen **tümünü** https://kernelsu.org/zh_CN/guide/installation.html adresin
## Lisans
- `kernel` dizinindeki dosyalar [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) lisansı altındadır.
- `kernel` dizini dışındaki tüm diğer bölümler [GPL-3.0 veya daha üstü](https://www.gnu.org/licenses/gpl-3.0.html) lisansı altındadır.
- Anime karakter ifadeleri içeren `ic_launcher(?!.*alt.*).*` dosyalarının görüntüleri [五十根大虾仁](https://space.bilibili.com/370927) tarafından telif hakkıyla korunmaktadır, görüntülerdeki Marka Fikri Mülkiyeti [明风 OuO](https://space.bilibili.com/274939213)'ye aittir ve vektörleştirme @MiRinChan tarafından yapılmıştır. Bu dosyaları kullanmadan önce, [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) ile uyumlu olmanın yanı sıra, bu sanatsal içerikleri kullanmak için iki yazarın yetkilendirmesine de uymanız gerekir.
- Yukarıda belirtilen dosyalar veya dizinler hariç, diğer tüm parçalar [GPL-3.0 veya üzeri](https://www.gnu.org/licenses/gpl-3.0.html)'dir.
## Afdian Bağlantısı
- https://afdian.com/a/shirkneko
## Sponsor Listesi
@@ -127,8 +136,6 @@ Lütfen **tümünü** https://kernelsu.org/zh_CN/guide/installation.html adresin
- [yspbwx2010](https://github.com/yspbwx2010) Çok teşekkür ederim
- [DARKWWEE](https://github.com/DARKWWEE) 100 USDT için teşekkürler
Eğer yukarıdaki listede adınız yoksa, zamanında güncelleyeceğim, herkese tekrar teşekkür ederim
## Katkıda Bulunanlar
- [KernelSU](https://github.com/tiann/KernelSU): Orijinal proje

View File

@@ -14,19 +14,21 @@
在内核源码的根目录下执行以下命令:
使用 main 分支 (不支持非GKI设备构建)
使用 main 分支 (不支持非 GKI 设备构建) (需要手动集成 susfs)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
```
使用支持非 GKI 设备的分支
使用支持非 GKI 设备的分支 (需要手动集成 susfs)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
```
## 如何集成 susfs
1. 直接使用 susfs-stable 或者 susfs-dev 分支,不需要再集成 susfs
1. 直接使用 susfs-stable 或者 susfs-dev 分支,不需要再集成 susfs (支持非 GKI 设备构建)
```
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
@@ -37,15 +39,16 @@ curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kern
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
1. **KPROBES 钩子:**
- 用于可加载内核模块 (LKM)
- GKI 2.0 内核的默认钩子方法
- 需要 `CONFIG_KPROBES=y`
- 用于可加载内核模块 (LKM)
- GKI 2.0 内核的默认钩子方法
- 需要 `CONFIG_KPROBES=y`
2. **手动钩子:**
- 标准的 KernelSU 钩子https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
- backslashxx 的 syscall 手动钩子https://github.com/backslashxx/KernelSU/issues/5
- 非 GKI 内核的默认挂钩方法
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
- 标准的 KernelSU 钩子https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
- backslashxx 的 syscall 手动钩子https://github.com/backslashxx/KernelSU/issues/5
- 非 GKI 内核的默认挂钩方法
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
## KPM 支持
@@ -57,16 +60,18 @@ curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kern
KPM 模板地址: https://github.com/udochina/KPM-Build-Anywhere
> [!Note]
>
> 1. 需要 `CONFIG_KPM=y`
> 2. 非GKI设备还需要 `CONFIG_KALLSYMS=y` 和 `CONFIG_KALLSYMS_ALL=y`
> 2. 非 GKI 设备还需要 `CONFIG_KALLSYMS=y` 和 `CONFIG_KALLSYMS_ALL=y`
> 3. 部分内核 `4.19` 以下源码还需要从 `4.19` 向后移植头文件 `set_memory.h`
## 如何进行系统更新保留 ROOT
## 如何进行系统更新保留ROOT
- OTA后先不要重启进入管理器刷写/修补内核界面,找到 `GKI/non_GKI安装` 选择需要刷写的Anykernel3内核压缩文件选择与现在系统运行槽位相反的槽位进行刷写并重启即可保留GKI模式更新暂不支持所有非GKI设备使用这种方法请自行尝试。非GKI设备使用TWRP刷写是最稳妥的
- 或者使用LKM模式的安装到未使用的槽位OTA后
- OTA 后先不要重启,进入管理器刷写/修补内核界面,找到 `GKI/non_GKI安装` 选择需要刷写的 Anykernel3 内核压缩文件,选择与现在系统运行槽位相反的槽位进行刷写并重启即可保留 GKI 模式更新(暂不支持所有非 GKI 设备使用这种方法,请自行尝试。非 GKI 设备使用 TWRP 刷写是最稳妥的)
- 或者使用 LKM 模式的安装到未使用的槽位OTA 后
## 兼容状态
- KernelSUv1.0.0 之前版本)正式支持 Android GKI 2.0 设备(内核 5.10+
- 旧内核4.4+)也兼容,但必须手动构建内核
@@ -77,7 +82,10 @@ KPM 模板地址: https://github.com/udochina/KPM-Build-Anywhere
## 更多链接
**如果你需要为管理器提交翻译请前往** https://crowdin.com/project/SukiSU-Ultra
基于 SukiSU 和 susfs 编译的项目
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
- [一加](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
@@ -88,16 +96,17 @@ KPM 模板地址: https://github.com/udochina/KPM-Build-Anywhere
请**全部**参考 https://kernelsu.org/zh_CN/guide/installation.html
> [!Note]
>
> 1. 适用于如小米、红米、三星等的 GKI 2.0 的设备 (不包含魔改内核的厂商如魅族、一加、真我和 oppo)
> 2. 找到[更多链接](#%E6%9B%B4%E5%A4%9A%E9%93%BE%E6%8E%A5)里的 GKI 构建的项目。找到设备内核版本。然后下载下来用TWRP或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可
> 2. 找到[更多链接](#%E6%9B%B4%E5%A4%9A%E9%93%BE%E6%8E%A5)里的 GKI 构建的项目。找到设备内核版本。然后下载下来,用 TWRP 或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可
> 3. 一般不带后缀的 .zip 压缩包是未压缩的gz 后缀的为天玑机型所使用的压缩方式
### 一加
1.找到更多链接里的一加项目进行自行填写,然后云编译构建,最后刷入带 AnyKernel3 后缀的压缩包即可
> [!Note]
>
> - 内核版本只需要填写前两位即可,如 5.105.156.16.6
> - 处理器代号请自行搜索,一般为全英文不带数字的代号
> - 分支和配置文件请自行到一加内核开源地址进行填写
@@ -111,12 +120,17 @@ KPM 模板地址: https://github.com/udochina/KPM-Build-Anywhere
5. 更多自定义功能
6. 对 KPM 内核模块的支持
## 疑难解答
1. 卸载 KernelSU 管理器设备卡死。→ 卸载包名为 com.sony.playmemories.mobile 的应用。
## 许可证
- `kernel` 目录下的文件是 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
- `kernel` 目录外,所有其他部分均为 [GPL-3.0 或更高版本](https://www.gnu.org/licenses/gpl-3.0.html)
- 有动漫人物图片表情包的这些文件 `ic_launcher(?!.*alt.*).*` 的图像版权为[五十根大虾仁](https://space.bilibili.com/370927)所有,图像中的 Brand Intellectual Property 由[明风OuO](https://space.bilibili.com/274939213)所有,矢量化由 @MiRinChan 完成,在使用这些文件之前,除了必须遵守 [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) 以外,还需要遵守向前两者索要使用这些艺术内容的授权
- 除了以上所述的文件或目录外,所有其他部分均为 [GPL-3.0 或更高版本](https://www.gnu.org/licenses/gpl-3.0.html)。
## 爱发电链接
- https://afdian.com/a/shirkneko
## 赞助名单
@@ -126,14 +140,14 @@ KPM 模板地址: https://github.com/udochina/KPM-Build-Anywhere
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
- [yspbwx2010](https://github.com/yspbwx2010) 非常感谢
- [DARKWWEE](https://github.com/DARKWWEE) 感谢老哥的 100 USDT
如果以上名单没有你的名称,我会及时更新,再次感谢大家的支持
- [Saksham Singla](https://github.com/TypeFlu) 网站的提供以及维护
- [OukaroMF](https://github.com/OukaroMF) 网站域名捐赠
## 贡献
- [KernelSU](https://github.com/tiann/KernelSU):原始项目
- [MKSU](https://github.com/5ec1cff/KernelSU):使用的项目
- [RKSU](https://github.com/rsuntk/KernelsU):使用该项目的 kernel 对非GKI设备重新进行支持
- [RKSU](https://github.com/rsuntk/KernelsU):使用该项目的 kernel 对非 GKI 设备重新进行支持
- [susfs4ksu](https://gitlab.com/simonpunk/susfs4ksu):使用的 susfs 文件系统
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/)KernelSU 的构想
- [Magisk](https://github.com/topjohnwu/Magisk):强大的 root 工具

View File

@@ -226,6 +226,7 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
return 0;
}
#ifdef CONFIG_EXT4_FS
static void nuke_ext4_sysfs() {
struct path path;
int err = kern_path("/data/adb/modules", 0, &path);
@@ -243,6 +244,9 @@ static void nuke_ext4_sysfs() {
ext4_unregister_sysfs(sb);
}
#else
static inline void nuke_ext4_sysfs() { }
#endif
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
@@ -425,6 +429,13 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
}
#endif
if (arg2 == CMD_ENABLE_KPM) {
bool KPM_Enabled = IS_ENABLED(CONFIG_KPM);
if (copy_to_user((void __user *)arg3, &KPM_Enabled, sizeof(KPM_Enabled)))
pr_info("KPM: copy_to_user() failed\n");
return 0;
}
// all other cmds are for 'root manager'
if (!from_manager) {
return 0;

View File

@@ -23,6 +23,7 @@
#define CMD_UID_SHOULD_UMOUNT 13
#define CMD_IS_SU_ENABLED 14
#define CMD_ENABLE_SU 15
#define CMD_ENABLE_KPM 100
#define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2

View File

@@ -5,6 +5,7 @@
#include <linux/string.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/namei.h>
#include "allowlist.h"
#include "klog.h" // IWYU pragma: keep
@@ -115,6 +116,7 @@ struct my_dir_context {
void *private_data;
int depth;
int *stop;
struct super_block* root_sb;
};
// https://docs.kernel.org/filesystems/porting.html
// filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: callers never looked at specific -E... values anyway. -> iterate_shared() instances require no changes at all, all filldir_t ones in the tree converted.
@@ -135,6 +137,8 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
struct my_dir_context *my_ctx =
container_of(ctx, struct my_dir_context, ctx);
char dirpath[DATA_PATH_LEN];
int err;
struct path path;
if (!my_ctx) {
pr_err("Invalid context\n");
@@ -162,6 +166,18 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
return FILLDIR_ACTOR_CONTINUE;
}
err = kern_path(dirpath, 0, &path);
if (err) {
pr_err("get dirpath %s err: %d\n", dirpath, err);
return FILLDIR_ACTOR_CONTINUE;
}
if (my_ctx->root_sb != path.dentry->d_inode->i_sb) {
pr_info("skip cross fs: %s", dirpath);
return FILLDIR_ACTOR_CONTINUE;
}
if (d_type == DT_DIR && my_ctx->depth > 0 &&
(my_ctx->stop && !*my_ctx->stop)) {
struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC);
@@ -211,10 +227,19 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
void search_manager(const char *path, int depth, struct list_head *uid_data)
{
int i, stop = 0;
int i, stop = 0, err;
struct list_head data_path_list;
struct path kpath;
struct super_block* root_sb;
INIT_LIST_HEAD(&data_path_list);
err = kern_path(path, 0, &kpath);
if (err) {
pr_err("get search root %s err: %d\n", path, err);
return;
}
// Initialize APK cache list
struct apk_path_hash *pos, *n;
list_for_each_entry(pos, &apk_path_hash_list, list) {
@@ -227,6 +252,8 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
data.depth = depth;
list_add_tail(&data.list, &data_path_list);
root_sb = kpath.dentry->d_inode->i_sb;
for (i = depth; i >= 0; i--) {
struct data_path *pos, *n;
@@ -236,7 +263,8 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
.parent_dir = pos->dirpath,
.private_data = uid_data,
.depth = pos->depth,
.stop = &stop };
.stop = &stop,
.root_sb = root_sb };
struct file *file;
if (!stop) {

View File

@@ -50,7 +50,6 @@ android {
}
buildFeatures {
aidl = true
buildConfig = true
compose = true
prefab = true
@@ -109,6 +108,7 @@ android {
}
dependencies {
implementation(libs.gson)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.navigation.compose)

View File

@@ -32,6 +32,19 @@
</intent-filter>
</activity>
<activity-alias
android:name=".ui.MainActivityAlias"
android:exported="true"
android:enabled="false"
android:icon="@mipmap/ic_launcher_alt"
android:roundIcon="@mipmap/ic_launcher_alt_round"
android:targetActivity=".ui.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity
android:name=".ui.webui.WebUIActivity"
android:autoRemoveFromRecents="true"

View File

@@ -1,8 +0,0 @@
package com.sukisu.zako;
import android.content.pm.PackageInfo;
import rikka.parcelablelist.ParcelableListSlice;
interface IKsuInterface {
ParcelableListSlice<PackageInfo> getPackages(int flags);
}

View File

@@ -306,3 +306,8 @@ JNIEXPORT jboolean JNICALL
Java_com_sukisu_ultra_Natives_setSuEnabled(JNIEnv *env, jobject thiz, jboolean enabled) {
return set_su_enabled(enabled);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_sukisu_ultra_Natives_isKPMEnabled(JNIEnv *env, jobject) {
return is_KPM_enable();
}

View File

@@ -29,6 +29,7 @@
#define CMD_IS_UID_SHOULD_UMOUNT 13
#define CMD_IS_SU_ENABLED 14
#define CMD_ENABLE_SU 15
#define CMD_ENABLE_KPM 100
static bool ksuctl(int cmd, void* arg1, void* arg2) {
int32_t result = 0;
@@ -97,3 +98,8 @@ bool is_su_enabled() {
ksuctl(CMD_IS_SU_ENABLED, &enabled, nullptr);
return enabled;
}
bool is_KPM_enable() {
bool enabled = false;
return ksuctl(CMD_ENABLE_KPM, &enabled, nullptr), enabled;
}

View File

@@ -83,4 +83,6 @@ bool set_su_enabled(bool enabled);
bool is_su_enabled();
bool is_KPM_enable();
#endif //KERNELSU_KSU_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -1,11 +1,14 @@
package com.sukisu.ultra
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityOptions
import android.app.Application
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import android.os.Bundle
import coil.Coil
import coil.ImageLoader
import com.dergoogler.mmrl.platform.Platform
@@ -14,9 +17,31 @@ import me.zhanghai.android.appiconloader.coil.AppIconKeyer
import java.io.File
import java.util.Locale
@SuppressLint("StaticFieldLeak")
lateinit var ksuApp: KernelSUApplication
class KernelSUApplication : Application() {
private var currentActivity: Activity? = null
private val activityLifecycleCallbacks = object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
currentActivity = activity
}
override fun onActivityStarted(activity: Activity) {
currentActivity = activity
}
override fun onActivityResumed(activity: Activity) {
currentActivity = activity
}
override fun onActivityPaused(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {
if (currentActivity == activity) {
currentActivity = null
}
}
}
override fun attachBaseContext(base: Context) {
val prefs = base.getSharedPreferences("settings", MODE_PRIVATE)
@@ -62,6 +87,9 @@ class KernelSUApplication : Application() {
super.onCreate()
ksuApp = this
// 注册Activity生命周期回调
registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
Platform.setHiddenApiExemptions()
val context = this
@@ -107,4 +135,17 @@ class KernelSUApplication : Application() {
}
}
}
// 添加刷新当前Activity的方法
fun refreshCurrentActivity() {
currentActivity?.let { activity ->
val intent = activity.intent
activity.finish()
val options = ActivityOptions.makeCustomAnimation(
activity, android.R.anim.fade_in, android.R.anim.fade_out
)
activity.startActivity(intent, options.toBundle())
}
}
}

View File

@@ -8,24 +8,13 @@ import android.system.Os
*/
data class KernelVersion(val major: Int, val patchLevel: Int, val subLevel: Int) {
override fun toString(): String {
return "$major.$patchLevel.$subLevel"
}
fun isGKI(): Boolean {
// kernel 6.x
if (major > 5) {
return true
}
// kernel 5.10.x
if (major == 5) {
return patchLevel >= 10
}
return false
override fun toString(): String = "$major.$patchLevel.$subLevel"
fun isGKI(): Boolean = when {
major > 5 -> true
major == 5 && patchLevel >= 10 -> true
else -> false
}
fun isGKI1(): Boolean = (major == 4 && patchLevel >= 19) || (major == 5 && patchLevel < 10)
}
fun parseKernelVersion(version: String): KernelVersion {

View File

@@ -26,6 +26,8 @@ object Natives {
const val MINIMAL_SUPPORTED_SU_COMPAT = 12040
const val KERNEL_SU_DOMAIN = "u:r:su:s0"
const val MINIMAL_SUPPORTED_KPM = 12800
const val ROOT_UID = 0
const val ROOT_GID = 0
@@ -66,6 +68,7 @@ object Natives {
*/
external fun isSuEnabled(): Boolean
external fun setSuEnabled(enabled: Boolean): Boolean
external fun isKPMEnabled(): Boolean
private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$"
private const val NOBODY_UID = 9999

View File

@@ -1,200 +1,183 @@
package com.sukisu.ultra.ui
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.database.ContentObserver
import android.os.Build
import android.os.Bundle
import android.os.Handler
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController
import androidx.lifecycle.lifecycleScope
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle
import com.ramcosta.composedestinations.generated.NavGraphs
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
import com.ramcosta.composedestinations.spec.NavHostGraphSpec
import com.ramcosta.composedestinations.spec.RouteOrDirection
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
import io.sukisu.ultra.UltraToolInstall
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.screen.BottomBarDestination
import zako.zako.zako.zakoui.activity.util.AppData
import com.sukisu.ultra.ui.theme.*
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.util.*
import androidx.core.content.edit
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import zako.zako.zako.zakoui.activity.util.*
import zako.zako.zako.zakoui.activity.component.BottomBar
import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.install
import com.sukisu.ultra.ui.viewmodel.HomeViewModel
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.sukisu.ultra.ui.webui.initPlatform
import java.util.Locale
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
private inner class ThemeChangeContentObserver(
handler: Handler,
private val onThemeChanged: () -> Unit
) : ContentObserver(handler) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
onThemeChanged()
}
}
private lateinit var superUserViewModel: SuperUserViewModel
private lateinit var homeViewModel: HomeViewModel
internal val settingsStateFlow = MutableStateFlow(SettingsState())
// 应用保存的语言设置
@SuppressLint("ObsoleteSdkInt")
private fun applyLanguageSetting() {
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
val languageCode = prefs.getString("app_language", "") ?: ""
data class SettingsState(
val isHideOtherInfo: Boolean = false,
val showKpmInfo: Boolean = false
)
if (languageCode.isNotEmpty()) {
val locale = Locale.forLanguageTag(languageCode)
Locale.setDefault(locale)
private lateinit var themeChangeObserver: ThemeChangeContentObserver
val resources = resources
val config = Configuration(resources.configuration)
config.setLocale(locale)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
createConfigurationContext(config)
} else {
@Suppress("DEPRECATION")
resources.updateConfiguration(config, resources.displayMetrics)
}
}
}
// 添加标记避免重复初始化
private var isInitialized = false
override fun attachBaseContext(newBase: Context) {
val prefs = newBase.getSharedPreferences("settings", MODE_PRIVATE)
val languageCode = prefs.getString("app_language", "") ?: ""
var context = newBase
if (languageCode.isNotEmpty()) {
val locale = Locale.forLanguageTag(languageCode)
Locale.setDefault(locale)
val config = Configuration(newBase.resources.configuration)
config.setLocale(locale)
context = newBase.createConfigurationContext(config)
}
val context = LocaleUtils.applyLocale(newBase)
super.attachBaseContext(context)
}
override fun onCreate(savedInstanceState: Bundle?) {
// 确保应用正确的语言设置
applyLanguageSetting()
try {
// 确保应用正确的语言设置
LocaleUtils.applyLanguageSetting(this)
applyCustomDpi()
// 应用自定义 DPI
DisplayUtils.applyCustomDpi(this)
// Enable edge to edge
enableEdgeToEdge()
// Enable edge to edge
enableEdgeToEdge()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isNavigationBarContrastEnforced = false
}
super.onCreate(savedInstanceState)
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
val isFirstRun = prefs.getBoolean("is_first_run", true)
if (isFirstRun) {
ThemeConfig.preventBackgroundRefresh = false
getSharedPreferences("theme_prefs", MODE_PRIVATE).edit {
putBoolean("prevent_background_refresh", false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isNavigationBarContrastEnforced = false
}
prefs.edit { putBoolean("is_first_run", false) }
}
// 加载保存的背景设置
loadThemeMode()
loadThemeColors()
loadDynamicColorState()
CardConfig.load(applicationContext)
super.onCreate(savedInstanceState)
val contentObserver = ThemeChangeContentObserver(Handler(mainLooper)) {
runOnUiThread {
if (!ThemeConfig.preventBackgroundRefresh) {
ThemeConfig.backgroundImageLoaded = false
loadCustomBackground()
// 使用标记控制初始化流程
if (!isInitialized) {
initializeViewModels()
initializeData()
isInitialized = true
}
setContent {
KernelSUTheme {
val navController = rememberNavController()
val snackBarHostState = remember { SnackbarHostState() }
val currentDestination = navController.currentBackStackEntryAsState().value?.destination
val showBottomBar = when (currentDestination?.route) {
ExecuteModuleActionScreenDestination.route -> false
else -> true
}
LaunchedEffect(Unit) {
initPlatform()
}
CompositionLocalProvider(
LocalSnackbarHost provides snackBarHostState
) {
Scaffold(
bottomBar = {
AnimatedBottomBar.AnimatedBottomBarWrapper(
showBottomBar = showBottomBar,
content = { BottomBar(navController) }
)
},
contentWindowInsets = WindowInsets(0, 0, 0, 0)
) { innerPadding ->
DestinationsNavHost(
modifier = Modifier.padding(innerPadding),
navGraph = NavGraphs.root as NavHostGraphSpec,
navController = navController,
defaultTransitions = NavigationUtils.defaultTransitions()
)
}
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun initializeViewModels() {
superUserViewModel = SuperUserViewModel()
homeViewModel = HomeViewModel()
// 设置主题变化监听器
themeChangeObserver = ThemeUtils.registerThemeChangeObserver(this)
}
private fun initializeData() {
lifecycleScope.launch {
try {
superUserViewModel.fetchAppList()
} catch (e: Exception) {
e.printStackTrace()
}
}
contentResolver.registerContentObserver(
android.provider.Settings.System.getUriFor("ui_night_mode"),
false,
contentObserver
)
val destroyListeners = mutableListOf<() -> Unit>()
destroyListeners.add {
contentResolver.unregisterContentObserver(contentObserver)
lifecycleScope.launch {
try {
homeViewModel.initializeData()
} catch (e: Exception) {
e.printStackTrace()
}
}
val isManager = Natives.becomeManager(ksuApp.packageName)
// 数据刷新协程
DataRefreshUtils.startDataRefreshCoroutine(lifecycleScope)
DataRefreshUtils.startSettingsMonitorCoroutine(lifecycleScope, this, settingsStateFlow)
// 初始化主题相关设置
ThemeUtils.initializeThemeSettings(this, settingsStateFlow)
val isManager = AppData.isManager(ksuApp.packageName)
if (isManager) {
install()
UltraToolInstall.tryToInstall()
}
}
setContent {
KernelSUTheme {
val navController = rememberNavController()
val snackBarHostState = remember { SnackbarHostState() }
override fun onResume() {
try {
super.onResume()
LocaleUtils.applyLanguageSetting(this)
ThemeUtils.onActivityResume()
// pre-init platform to faster start WebUI X activities
LaunchedEffect(Unit) {
initPlatform()
}
Scaffold(
bottomBar = { BottomBar(navController) },
contentWindowInsets = WindowInsets(0, 0, 0, 0)
) { innerPadding ->
CompositionLocalProvider(
LocalSnackbarHost provides snackBarHostState
) {
DestinationsNavHost(
modifier = Modifier.padding(innerPadding),
navGraph = NavGraphs.root as NavHostGraphSpec,
navController = navController,
defaultTransitions = object : NavHostAnimatedDestinationStyle() {
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition
get() = { fadeIn(animationSpec = tween(340)) }
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition
get() = { fadeOut(animationSpec = tween(340)) }
}
)
}
}
// 仅在需要时刷新数据
if (isInitialized) {
refreshData()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
// 应用自定义DPI设置
private fun applyCustomDpi() {
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
val customDpi = prefs.getInt("app_dpi", 0)
if (customDpi > 0) {
private fun refreshData() {
lifecycleScope.launch {
try {
val resources = resources
val metrics = resources.displayMetrics
metrics.density = customDpi / 160f
@Suppress("DEPRECATION")
metrics.scaledDensity = customDpi / 160f
metrics.densityDpi = customDpi
superUserViewModel.fetchAppList()
homeViewModel.initializeData()
DataRefreshUtils.refreshData(lifecycleScope)
} catch (e: Exception) {
e.printStackTrace()
}
@@ -202,132 +185,29 @@ class MainActivity : ComponentActivity() {
}
override fun onPause() {
super.onPause()
CardConfig.save(applicationContext)
getSharedPreferences("theme_prefs", MODE_PRIVATE).edit {
putBoolean("prevent_background_refresh", true)
}
ThemeConfig.preventBackgroundRefresh = true
}
override fun onResume() {
super.onResume()
applyLanguageSetting()
if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) {
loadCustomBackground()
try {
super.onPause()
ThemeUtils.onActivityPause(this)
} catch (e: Exception) {
e.printStackTrace()
}
}
private val destroyListeners = mutableListOf<() -> Unit>()
override fun onDestroy() {
destroyListeners.forEach { it() }
super.onDestroy()
try {
ThemeUtils.unregisterThemeChangeObserver(this, themeChangeObserver)
super.onDestroy()
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
applyLanguageSetting()
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun BottomBar(navController: NavHostController) {
val navigator = navController.rememberDestinationsNavigator()
val isManager = Natives.becomeManager(ksuApp.packageName)
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
val kpmVersion = getKpmVersion()
val containerColor = MaterialTheme.colorScheme.surfaceVariant
val cardColor = MaterialTheme.colorScheme.surfaceVariant
// 检查是否显示KPM
val showKpmInfo = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
.getBoolean("show_kpm_info", true)
NavigationBar(
modifier = Modifier.windowInsetsPadding(
WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal)
),
containerColor = TopAppBarDefaults.topAppBarColors(
containerColor = cardColor.copy(alpha = cardAlpha),
scrolledContainerColor = containerColor.copy(alpha = cardAlpha)
).containerColor,
tonalElevation = cardElevation
) {
BottomBarDestination.entries.forEach { destination ->
if (destination == BottomBarDestination.Kpm) {
if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error") && showKpmInfo) {
if (!fullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem(
selected = isCurrentDestOnBackStack,
onClick = {
if (!isCurrentDestOnBackStack) {
navigator.navigate(destination.direction) {
popUpTo(NavGraphs.root as RouteOrDirection) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
},
icon = {
Icon(
imageVector = if (isCurrentDestOnBackStack) {
destination.iconSelected
} else {
destination.iconNotSelected
},
contentDescription = stringResource(destination.label),
tint = if (isCurrentDestOnBackStack) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
)
},
label = {
Text(
text = stringResource(destination.label),
style = MaterialTheme.typography.labelMedium
)
}
)
}
} else {
if (!fullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem(
selected = isCurrentDestOnBackStack,
onClick = {
if (!isCurrentDestOnBackStack) {
navigator.navigate(destination.direction) {
popUpTo(NavGraphs.root as RouteOrDirection) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
},
icon = {
Icon(
imageVector = if (isCurrentDestOnBackStack) {
destination.iconSelected
} else {
destination.iconNotSelected
},
contentDescription = stringResource(destination.label),
tint = if (isCurrentDestOnBackStack) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
)
},
label = {
Text(
text = stringResource(destination.label),
style = MaterialTheme.typography.labelMedium
)
}
)
}
try {
super.onConfigurationChanged(newConfig)
LocaleUtils.applyLanguageSetting(this)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View File

@@ -20,7 +20,6 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@@ -138,7 +137,7 @@ fun ImageEditorDialog(
0f
}
updateTransformation(newScale, newOffsetX, newOffsetY)
} catch (e: Exception) {
} catch (_: Exception) {
updateTransformation(lastScale, lastOffsetX, lastOffsetY)
}
}
@@ -186,7 +185,7 @@ fun ImageEditorDialog(
val transformation = BackgroundTransformation(scale, offsetX, offsetY)
val savedUri = context.saveTransformedBackground(imageUri, transformation)
savedUri?.let { onConfirm(it) }
} catch (e: Exception) {
} catch (_: Exception) {
""
}
}

View File

@@ -63,7 +63,12 @@ fun SearchAppBar(
var onSearch by remember { mutableStateOf(false) }
// 获取卡片颜色和透明度
val cardColor = MaterialTheme.colorScheme.surfaceVariant
val colorScheme = MaterialTheme.colorScheme
val cardColor = if (CardConfig.isCustomBackgroundEnabled) {
colorScheme.surfaceContainerLow
} else {
colorScheme.background
}
val cardAlpha = CardConfig.cardAlpha
if (onSearch) {

View File

@@ -13,10 +13,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.semantics.Role
import com.dergoogler.mmrl.ui.component.LabelItem
import com.dergoogler.mmrl.ui.component.text.TextRow
import com.sukisu.ultra.ui.theme.CardConfig
@Composable
fun SwitchItem(
@@ -26,65 +28,70 @@ fun SwitchItem(
checked: Boolean,
enabled: Boolean = true,
beta: Boolean = false,
onCheckedChange: (Boolean) -> Unit,
onCheckedChange: (Boolean) -> Unit
) {
val interactionSource = remember { MutableInteractionSource() }
val stateAlpha = remember(checked, enabled) { Modifier.alpha(if (enabled) 1f else 0.5f) }
ListItem(
modifier = Modifier
.toggleable(
value = checked,
interactionSource = interactionSource,
role = Role.Switch,
enabled = enabled,
indication = LocalIndication.current,
onValueChange = onCheckedChange
),
headlineContent = {
TextRow(
leadingContent = if (beta) {
{
LabelItem(
modifier = Modifier.then(stateAlpha),
text = "Beta"
)
}
} else null
) {
Text(
modifier = Modifier.then(stateAlpha),
text = title,
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else MaterialTheme.colorScheme.surfaceContainerHigh
)
) {
ListItem(
modifier = Modifier
.toggleable(
value = checked,
interactionSource = interactionSource,
role = Role.Switch,
enabled = enabled,
indication = LocalIndication.current,
onValueChange = onCheckedChange
),
headlineContent = {
TextRow(
leadingContent = if (beta) {
{
LabelItem(
modifier = Modifier.then(stateAlpha),
text = "Beta"
)
}
} else null
) {
Text(
modifier = Modifier.then(stateAlpha),
text = title,
)
}
},
leadingContent = icon?.let {
{
Icon(
modifier = Modifier.then(stateAlpha),
imageVector = icon,
contentDescription = title
)
}
},
trailingContent = {
Switch(
checked = checked,
enabled = enabled,
onCheckedChange = onCheckedChange,
interactionSource = interactionSource
)
},
supportingContent = {
if (summary != null) {
Text(
modifier = Modifier.then(stateAlpha),
text = summary
)
}
}
},
leadingContent = icon?.let {
{
Icon(
modifier = Modifier.then(stateAlpha),
imageVector = icon,
contentDescription = title,
tint = MaterialTheme.colorScheme.primary
)
}
},
trailingContent = {
Switch(
checked = checked,
enabled = enabled,
onCheckedChange = onCheckedChange,
interactionSource = interactionSource
)
},
supportingContent = {
if (summary != null) {
Text(
modifier = Modifier.then(stateAlpha),
text = summary
)
}
}
)
)
}
}
@Composable

View File

@@ -1,6 +1,5 @@
package com.sukisu.ultra.ui.component
import android.content.Context
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
@@ -10,12 +9,10 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.theme.ThemeConfig
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.SdStorage
import androidx.compose.ui.draw.clip
@@ -32,13 +29,19 @@ fun SlotSelectionDialog(
onDismiss: () -> Unit,
onSlotSelected: (String) -> Unit
) {
val context = LocalContext.current
var currentSlot by remember { mutableStateOf<String?>(null) }
var errorMessage by remember { mutableStateOf<String?>(null) }
var selectedSlot by remember { mutableStateOf<String?>(null) }
LaunchedEffect(Unit) {
try {
currentSlot = getCurrentSlot(context)
currentSlot = getCurrentSlot()
// 设置默认选择为当前槽位
selectedSlot = when (currentSlot) {
"a" -> "a"
"b" -> "b"
else -> null
}
errorMessage = null
} catch (e: Exception) {
errorMessage = e.message
@@ -103,12 +106,12 @@ fun SlotSelectionDialog(
val slotOptions = listOf(
ListOption(
titleText = stringResource(id = R.string.slot_a),
subtitleText = if (currentSlot == "a" || currentSlot == "_a") stringResource(id = R.string.currently_selected) else null,
subtitleText = null,
icon = Icons.Filled.SdStorage
),
ListOption(
titleText = stringResource(id = R.string.slot_b),
subtitleText = if (currentSlot == "b" || currentSlot == "_b") stringResource(id = R.string.currently_selected) else null,
subtitleText = null,
icon = Icons.Filled.SdStorage
)
)
@@ -124,19 +127,20 @@ fun SlotSelectionDialog(
.fillMaxWidth()
.clip(MaterialTheme.shapes.medium)
.background(
color = if (option.subtitleText != null) {
color = if (selectedSlot == when(index) {
0 -> "a"
else -> "b"
}) {
MaterialTheme.colorScheme.primary.copy(alpha = 0.9f)
} else {
MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.3f)
}
)
.clickable {
onSlotSelected(
when (index) {
0 -> "a"
else -> "b"
}
)
selectedSlot = when(index) {
0 -> "a"
else -> "b"
}
}
.padding(vertical = 12.dp, horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
@@ -144,7 +148,10 @@ fun SlotSelectionDialog(
Icon(
imageVector = option.icon,
contentDescription = null,
tint = if (option.subtitleText != null) {
tint = if (selectedSlot == when(index) {
0 -> "a"
else -> "b"
}) {
MaterialTheme.colorScheme.onPrimary
} else {
MaterialTheme.colorScheme.primary
@@ -159,7 +166,10 @@ fun SlotSelectionDialog(
Text(
text = option.titleText,
style = MaterialTheme.typography.titleMedium,
color = if (option.subtitleText != null) {
color = if (selectedSlot == when(index) {
0 -> "a"
else -> "b"
}) {
MaterialTheme.colorScheme.onPrimary
} else {
MaterialTheme.colorScheme.primary
@@ -169,7 +179,10 @@ fun SlotSelectionDialog(
Text(
text = it,
style = MaterialTheme.typography.bodyMedium,
color = if (true) {
color = if (selectedSlot == when(index) {
0 -> "a"
else -> "b"
}) {
MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.8f)
} else {
MaterialTheme.colorScheme.onSurfaceVariant
@@ -186,9 +199,10 @@ fun SlotSelectionDialog(
confirmButton = {
TextButton(
onClick = {
currentSlot?.let { onSlotSelected(it) }
selectedSlot?.let { onSlotSelected(it) }
onDismiss()
}
},
enabled = selectedSlot != null
) {
Text(
text = stringResource(android.R.string.ok),
@@ -221,7 +235,7 @@ data class ListOption(
)
// Utility function to get current slot
private fun getCurrentSlot(context: Context): String? {
private fun getCurrentSlot(): String? {
return runCommandGetOutput(true, "getprop ro.boot.slot_suffix")?.let {
if (it.startsWith("_")) it.substring(1) else it
}

View File

@@ -1,101 +0,0 @@
package com.sukisu.ultra.ui.component
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@Composable
fun SwitchItem(
icon: ImageVector,
title: String,
summary: String? = null,
checked: Boolean,
enabled: Boolean = true,
onCheckedChange: (Boolean) -> Unit
) {
// 颜色动画
val iconTint by animateColorAsState(
targetValue = if (checked) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
animationSpec = tween(300),
label = "iconTint"
)
// 开关颜色
val switchColors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colorScheme.primary,
checkedTrackColor = MaterialTheme.colorScheme.primaryContainer,
checkedBorderColor = MaterialTheme.colorScheme.primary,
checkedIconColor = MaterialTheme.colorScheme.onPrimary,
uncheckedThumbColor = MaterialTheme.colorScheme.outline,
uncheckedTrackColor = MaterialTheme.colorScheme.surfaceVariant,
uncheckedBorderColor = MaterialTheme.colorScheme.outline,
uncheckedIconColor = MaterialTheme.colorScheme.surfaceVariant,
disabledCheckedThumbColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f),
disabledCheckedTrackColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
disabledCheckedBorderColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
disabledCheckedIconColor = MaterialTheme.colorScheme.surfaceVariant,
disabledUncheckedThumbColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f),
disabledUncheckedTrackColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
disabledUncheckedBorderColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
disabledUncheckedIconColor = MaterialTheme.colorScheme.surfaceVariant
)
ListItem(
headlineContent = {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
maxLines = Int.MAX_VALUE,
overflow = TextOverflow.Ellipsis
)
},
supportingContent = summary?.let {
{
Text(
text = it,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = Int.MAX_VALUE,
overflow = TextOverflow.Ellipsis
)
}
},
leadingContent = {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(24.dp),
tint = iconTint
)
},
trailingContent = {
Switch(
checked = checked,
onCheckedChange = null,
enabled = enabled,
colors = switchColors
)
},
modifier = Modifier
.fillMaxWidth()
.clickable(enabled = enabled) {
onCheckedChange(!checked)
}
.padding(vertical = 4.dp)
)
}

View File

@@ -31,7 +31,6 @@ fun AppProfileConfig(
onValueChange = { onProfileChange(profile.copy(name = it)) }
)
}
SwitchItem(
title = stringResource(R.string.profile_umount_modules),
summary = stringResource(R.string.profile_umount_modules_summary),

View File

@@ -24,6 +24,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
@@ -47,6 +48,8 @@ import com.sukisu.ultra.R
import com.sukisu.ultra.profile.Capabilities
import com.sukisu.ultra.profile.Groups
import com.sukisu.ultra.ui.component.rememberCustomDialog
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.util.isSepolicyValid
@OptIn(ExperimentalMaterial3Api::class)
@@ -206,7 +209,7 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
}
val selection = HashSet(selected)
val backgroundColor = MaterialTheme.colorScheme.surfaceContainerHighest
val backgroundColor = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else MaterialTheme.colorScheme.surfaceContainerHigh
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
@@ -286,7 +289,7 @@ fun CapsPanel(
}
val selection = HashSet(selected)
val backgroundColor = MaterialTheme.colorScheme.surfaceContainerHighest
val backgroundColor = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else MaterialTheme.colorScheme.surfaceContainerHigh
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
@@ -441,7 +444,7 @@ private fun SELinuxPanel(
)
)
val backgroundColor = MaterialTheme.colorScheme.surfaceContainerHighest
val backgroundColor = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else MaterialTheme.colorScheme.surfaceContainerHigh
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(

View File

@@ -37,7 +37,6 @@ import androidx.compose.material3.FilterChip
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
@@ -60,6 +59,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
@@ -84,6 +84,9 @@ import com.sukisu.ultra.ui.component.profile.AppProfileConfig
import com.sukisu.ultra.ui.component.profile.RootProfileConfig
import com.sukisu.ultra.ui.component.profile.TemplateConfig
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation
import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.forceStopApp
import com.sukisu.ultra.ui.util.getSepolicy
@@ -122,7 +125,12 @@ fun AppProfileScreen(
mutableStateOf(initialProfile)
}
val cardColor = MaterialTheme.colorScheme.surfaceVariant
val colorScheme = MaterialTheme.colorScheme
val cardColor = if (CardConfig.isCustomBackgroundEnabled) {
colorScheme.surfaceContainerLow
} else {
colorScheme.background
}
val cardAlpha = CardConfig.cardAlpha
Scaffold(
@@ -203,149 +211,173 @@ private fun AppProfileInner(
onProfileChange: (Natives.Profile) -> Unit,
) {
val isRootGranted = profile.allowSu
val cardColors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh)
Column(modifier = modifier) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium
) {
AppMenuBox(packageName) {
ListItem(
headlineContent = {
Text(
text = appLabel,
style = MaterialTheme.typography.titleMedium
)
},
supportingContent = {
Text(
text = packageName,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
},
leadingContent = appIcon,
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else MaterialTheme.colorScheme.surfaceContainerHigh
)
) {
Column(modifier = modifier) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium,
colors = cardColors,
elevation = getCardElevation(),
) {
AppMenuBox(packageName) {
ListItem(
headlineContent = {
Text(
text = appLabel,
style = MaterialTheme.typography.titleMedium
)
},
supportingContent = {
Text(
text = packageName,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
},
leadingContent = appIcon,
)
}
}
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium,
colors = cardColors,
elevation = getCardElevation(),
) {
SwitchItem(
icon = Icons.Filled.Security,
title = stringResource(id = R.string.superuser),
checked = isRootGranted,
onCheckedChange = { onProfileChange(profile.copy(allowSu = it)) },
)
}
}
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium
) {
SwitchItem(
icon = Icons.Filled.Security,
title = stringResource(id = R.string.superuser),
checked = isRootGranted,
onCheckedChange = { onProfileChange(profile.copy(allowSu = it)) },
)
}
Crossfade(
targetState = isRootGranted,
label = "RootAccess"
) { current ->
Column(
modifier = Modifier.padding(bottom = 6.dp + 48.dp + 6.dp /* SnackBar height */)
) {
if (current) {
val initialMode = if (profile.rootUseDefault) {
Mode.Default
} else if (profile.rootTemplate != null) {
Mode.Template
} else {
Mode.Custom
}
var mode by rememberSaveable {
mutableStateOf(initialMode)
}
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium
) {
ProfileBox(mode, true) {
// template mode shouldn't change profile here!
if (it == Mode.Default || it == Mode.Custom) {
onProfileChange(profile.copy(rootUseDefault = it == Mode.Default))
}
mode = it
Crossfade(
targetState = isRootGranted,
label = "RootAccess"
) { current ->
Column(
modifier = Modifier.padding(bottom = 6.dp + 48.dp + 6.dp /* SnackBar height */)
) {
if (current) {
val initialMode = if (profile.rootUseDefault) {
Mode.Default
} else if (profile.rootTemplate != null) {
Mode.Template
} else {
Mode.Custom
}
var mode by rememberSaveable {
mutableStateOf(initialMode)
}
}
AnimatedVisibility(
visible = mode != Mode.Default,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium
shape = MaterialTheme.shapes.medium,
colors = cardColors,
elevation = getCardElevation(),
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Crossfade(targetState = mode, label = "ProfileMode") { currentMode ->
when (currentMode) {
Mode.Template -> {
TemplateConfig(
profile = profile,
onViewTemplate = onViewTemplate,
onManageTemplate = onManageTemplate,
onProfileChange = onProfileChange
)
ProfileBox(mode, true) {
// template mode shouldn't change profile here!
if (it == Mode.Default || it == Mode.Custom) {
onProfileChange(profile.copy(rootUseDefault = it == Mode.Default))
}
mode = it
}
}
AnimatedVisibility(
visible = mode != Mode.Default,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium,
colors = cardColors,
elevation = getCardElevation(),
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Crossfade(
targetState = mode,
label = "ProfileMode"
) { currentMode ->
when (currentMode) {
Mode.Template -> {
TemplateConfig(
profile = profile,
onViewTemplate = onViewTemplate,
onManageTemplate = onManageTemplate,
onProfileChange = onProfileChange
)
}
Mode.Custom -> {
RootProfileConfig(
fixedName = true,
profile = profile,
onProfileChange = onProfileChange
)
}
else -> {}
}
Mode.Custom -> {
RootProfileConfig(
fixedName = true,
profile = profile,
onProfileChange = onProfileChange
)
}
else -> {}
}
}
}
}
}
} else {
val mode = if (profile.nonRootUseDefault) Mode.Default else Mode.Custom
} else {
val mode = if (profile.nonRootUseDefault) Mode.Default else Mode.Custom
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium
) {
ProfileBox(mode, false) {
onProfileChange(profile.copy(nonRootUseDefault = (it == Mode.Default)))
}
}
AnimatedVisibility(
visible = mode == Mode.Custom,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium
shape = MaterialTheme.shapes.medium,
colors = cardColors,
elevation = getCardElevation(),
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
AppProfileConfig(
fixedName = true,
profile = profile,
enabled = mode == Mode.Custom,
onProfileChange = onProfileChange
)
ProfileBox(mode, false) {
onProfileChange(profile.copy(nonRootUseDefault = (it == Mode.Default)))
}
}
AnimatedVisibility(
visible = mode == Mode.Custom,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = MaterialTheme.shapes.medium,
colors = cardColors,
elevation = getCardElevation(),
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
AppProfileConfig(
fixedName = true,
profile = profile,
enabled = mode == Mode.Custom,
onProfileChange = onProfileChange
)
}
}
}
}
@@ -377,12 +409,10 @@ private fun TopBar(
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface
)
Text(
text = packageName,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.alpha(0.8f)
)
}
@@ -391,9 +421,6 @@ private fun TopBar(
navigationIcon = {
IconButton(
onClick = onBack,
colors = IconButtonDefaults.iconButtonColors(
contentColor = MaterialTheme.colorScheme.onSurface
)
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
@@ -408,7 +435,6 @@ private fun TopBar(
modifier = Modifier.shadow(
elevation = if ((scrollBehavior?.state?.overlappedFraction ?: 0f) > 0.01f)
4.dp else 0.dp,
spotColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)
)
)
}
@@ -431,7 +457,6 @@ private fun ProfileBox(
Text(
text = mode.text,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
},
leadingContent = {
@@ -444,7 +469,6 @@ private fun ProfileBox(
HorizontalDivider(
thickness = Dp.Hairline,
color = MaterialTheme.colorScheme.outlineVariant
)
ListItem(
@@ -574,20 +598,26 @@ private fun AppMenuOption(text: String, onClick: () -> Unit) {
@Composable
private fun AppProfilePreview() {
var profile by remember { mutableStateOf(Natives.Profile("")) }
Surface {
AppProfileInner(
packageName = "icu.nullptr.test",
appLabel = "Test",
appIcon = {
Icon(
imageVector = Icons.Filled.Android,
contentDescription = null,
)
},
profile = profile,
onProfileChange = {
profile = it
},
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else MaterialTheme.colorScheme.surfaceContainerHigh
)
) {
Surface {
AppProfileInner(
packageName = "icu.nullptr.test",
appLabel = "Test",
appIcon = {
Icon(
imageVector = Icons.Filled.Android,
contentDescription = null,
)
},
profile = profile,
onProfileChange = {
profile = it
},
)
}
}
}

View File

@@ -3,6 +3,7 @@ package com.sukisu.ultra.ui.screen
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.filled.Archive
import androidx.compose.material.icons.outlined.*
import androidx.compose.ui.graphics.vector.ImageVector
import com.ramcosta.composedestinations.generated.destinations.HomeScreenDestination
@@ -21,8 +22,8 @@ enum class BottomBarDestination(
val rootRequired: Boolean,
) {
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
Kpm(KpmScreenDestination, R.string.kpm_title, Icons.Filled.Build, Icons.Outlined.Build, true),
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true),
Kpm(KpmScreenDestination, R.string.kpm_title, Icons.Filled.Archive, Icons.Outlined.Archive, true),
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.AdminPanelSettings, Icons.Outlined.AdminPanelSettings, true),
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Extension, Icons.Outlined.Extension, true),
Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false),
}

View File

@@ -1,15 +1,22 @@
package com.sukisu.ultra.ui.screen
import android.os.Environment
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.only
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -30,7 +37,6 @@ import androidx.compose.ui.input.key.key
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@@ -55,7 +61,11 @@ fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String
val snackBarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
val scrollState = rememberScrollState()
var actionResult: Boolean
var isActionRunning by rememberSaveable { mutableStateOf(true) }
BackHandler(enabled = isActionRunning) {
// Disable back button if action is running
}
LaunchedEffect(Unit) {
if (text.isNotEmpty()) {
@@ -76,33 +86,43 @@ fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String
onStderr = {
logContent.append(it).append("\n")
}
).let {
actionResult = it
}
)
}
if (actionResult) navigator.popBackStack()
isActionRunning = false
}
Scaffold(
topBar = {
TopBar(
onBack = dropUnlessResumed {
navigator.popBackStack()
},
isActionRunning = isActionRunning,
onSave = {
scope.launch {
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
val date = format.format(Date())
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"KernelSU_module_action_log_${date}.log"
)
file.writeText(logContent.toString())
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
if (!isActionRunning) {
scope.launch {
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
val date = format.format(Date())
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"KernelSU_module_action_log_${date}.log"
)
file.writeText(logContent.toString())
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
}
}
}
)
},
floatingActionButton = {
if (!isActionRunning) {
ExtendedFloatingActionButton(
text = { Text(text = stringResource(R.string.close)) },
icon = { Icon(Icons.Filled.Close, contentDescription = null) },
onClick = {
navigator.popBackStack()
}
)
}
},
contentWindowInsets = WindowInsets.safeDrawing,
snackbarHost = { SnackbarHost(snackBarHost) }
) { innerPadding ->
KeyEventBlocker {
@@ -130,16 +150,14 @@ fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
private fun TopBar(isActionRunning: Boolean, onSave: () -> Unit = {}) {
TopAppBar(
title = { Text(stringResource(R.string.action)) },
navigationIcon = {
IconButton(
onClick = onBack
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
},
actions = {
IconButton(onClick = onSave) {
IconButton(
onClick = onSave,
enabled = !isActionRunning
) {
Icon(
imageVector = Icons.Filled.Save,
contentDescription = stringResource(id = R.string.save_log),

View File

@@ -47,11 +47,16 @@ import com.sukisu.ultra.ui.component.KeyEventBlocker
import com.sukisu.ultra.ui.util.*
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.viewmodel.ModuleViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.ZipInputStream
/**
* @author ShirkNeko
* @date 2025/5/31.
*/
enum class FlashingStatus {
FLASHING,
SUCCESS,
@@ -110,6 +115,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
val scope = rememberCoroutineScope()
val scrollState = rememberScrollState()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val viewModel: ModuleViewModel = viewModel()
val errorCodeString = stringResource(R.string.error_code)
val checkLogString = stringResource(R.string.check_log)
@@ -163,6 +169,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
}
} else {
setFlashingStatus(FlashingStatus.SUCCESS)
viewModel.markNeedRefresh()
}
if (showReboot) {
text += "\n\n\n"
@@ -194,11 +201,14 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
val onBack: () -> Unit = {
if (currentFlashingStatus.value != FlashingStatus.FLASHING) {
if (flashIt is FlashIt.FlashBoot) {
navigator.popBackStack()
if (flashIt is FlashIt.FlashModules) {
viewModel.markNeedRefresh()
viewModel.fetchModuleList()
navigator.navigate(ModuleScreenDestination)
} else {
navigator.navigate(ModuleScreenDestination) {
}
viewModel.markNeedRefresh()
viewModel.fetchModuleList()
navigator.popBackStack()
}
}
}
@@ -430,7 +440,12 @@ private fun TopBar(
onSave: () -> Unit = {},
scrollBehavior: TopAppBarScrollBehavior? = null
) {
val cardColor = MaterialTheme.colorScheme.surfaceVariant
val colorScheme = MaterialTheme.colorScheme
val cardColor = if (CardConfig.isCustomBackgroundEnabled) {
colorScheme.surfaceContainerLow
} else {
colorScheme.background
}
val cardAlpha = CardConfig.cardAlpha
val statusColor = when(status) {
@@ -493,26 +508,13 @@ private fun TopBar(
suspend fun getModuleNameFromUri(context: android.content.Context, uri: Uri): String {
return withContext(Dispatchers.IO) {
try {
val zipInputStream = ZipInputStream(context.contentResolver.openInputStream(uri))
var entry = zipInputStream.nextEntry
var name = context.getString(R.string.unknown_module)
while (entry != null) {
if (entry.name == "module.prop") {
val reader = java.io.BufferedReader(java.io.InputStreamReader(zipInputStream))
var line: String?
while (reader.readLine().also { line = it } != null) {
if (line?.startsWith("name=") == true) {
name = line.substringAfter("=")
break
}
}
break
}
entry = zipInputStream.nextEntry
if (uri == Uri.EMPTY) {
return@withContext context.getString(R.string.unknown_module)
}
zipInputStream.close()
name
if (!ModuleUtils.isUriAccessible(context, uri)) {
return@withContext context.getString(R.string.unknown_module)
}
ModuleUtils.extractModuleName(context, uri)
} catch (_: Exception) {
context.getString(R.string.unknown_module)
}

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,6 @@ import androidx.compose.material.icons.filled.FileUpload
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
@@ -51,7 +50,6 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
@@ -61,6 +59,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -75,12 +74,10 @@ import com.maxkeppeler.sheets.list.models.ListSelection
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.generated.destinations.KernelFlashScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import com.sukisu.ultra.R
import com.sukisu.ultra.flash.HorizonKernelFlashProgress
import com.sukisu.ultra.flash.HorizonKernelState
import com.sukisu.ultra.flash.HorizonKernelWorker
import com.sukisu.ultra.ui.component.DialogHandle
import com.sukisu.ultra.ui.component.SlotSelectionDialog
import com.sukisu.ultra.ui.component.rememberConfirmDialog
@@ -95,10 +92,12 @@ import com.sukisu.ultra.ui.util.isAbDevice
import com.sukisu.ultra.ui.util.isInitBoot
import com.sukisu.ultra.ui.util.rootAvailable
import com.sukisu.ultra.getKernelVersion
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.theme.getCardElevation
/**
* @author weishu
* @date 2024/3/12.
* @author ShirkNeko
* @date 2025/5/31.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@@ -110,16 +109,10 @@ fun InstallScreen(navigator: DestinationsNavigator) {
var showRebootDialog by remember { mutableStateOf(false) }
var showSlotSelectionDialog by remember { mutableStateOf(false) }
var tempKernelUri by remember { mutableStateOf<Uri?>(null) }
val horizonKernelState = remember { HorizonKernelState() }
val flashState by horizonKernelState.state.collectAsState()
val summary = stringResource(R.string.horizon_kernel_summary)
val kernelVersion = getKernelVersion()
val isGKI = kernelVersion.isGKI()
val isAbDevice = isAbDevice()
val onFlashComplete = {
showRebootDialog = true
}
val summary = stringResource(R.string.horizon_kernel_summary)
if (showRebootDialog) {
RebootDialog(
@@ -145,14 +138,12 @@ fun InstallScreen(navigator: DestinationsNavigator) {
when (method) {
is InstallMethod.HorizonKernel -> {
method.uri?.let { uri ->
val worker = HorizonKernelWorker(
context = context,
state = horizonKernelState,
slot = method.slot
navigator.navigate(
KernelFlashScreenDestination(
kernelUri = uri,
selectedSlot = method.slot
)
)
worker.uri = uri
worker.setOnFlashCompleteListener(onFlashComplete)
worker.start()
}
}
else -> {
@@ -202,21 +193,6 @@ fun InstallScreen(navigator: DestinationsNavigator) {
}
}
val selectLkmLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
it.data?.data?.let { uri ->
lkmSelection = LkmSelection.LkmUri(uri)
}
}
}
val onLkmUpload = {
selectLkmLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply {
type = "application/octet-stream"
})
}
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
@@ -224,7 +200,6 @@ fun InstallScreen(navigator: DestinationsNavigator) {
topBar = {
TopBar(
onBack = { navigator.popBackStack() },
onLkmUpload = onLkmUpload,
scrollBehavior = scrollBehavior
)
},
@@ -241,7 +216,6 @@ fun InstallScreen(navigator: DestinationsNavigator) {
) {
SelectInstallMethod(
isGKI = isGKI,
isAbDevice = isAbDevice,
onSelected = { method ->
if (method is InstallMethod.HorizonKernel && method.uri != null) {
if (isAbDevice) {
@@ -253,18 +227,9 @@ fun InstallScreen(navigator: DestinationsNavigator) {
} else {
installMethod = method
}
horizonKernelState.reset()
}
)
AnimatedVisibility(
visible = flashState.isFlashing && installMethod is InstallMethod.HorizonKernel,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
HorizonKernelFlashProgress(flashState)
}
Column(
modifier = Modifier
.fillMaxWidth()
@@ -273,7 +238,7 @@ fun InstallScreen(navigator: DestinationsNavigator) {
(lkmSelection as? LkmSelection.LkmUri)?.let {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation),
elevation = getCardElevation(),
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
@@ -299,7 +264,7 @@ fun InstallScreen(navigator: DestinationsNavigator) {
if (method.slot != null) {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation),
elevation = getCardElevation(),
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
@@ -325,7 +290,7 @@ fun InstallScreen(navigator: DestinationsNavigator) {
Button(
modifier = Modifier.fillMaxWidth(),
enabled = installMethod != null && !flashState.isFlashing,
enabled = installMethod != null,
onClick = onClickNext,
shape = MaterialTheme.shapes.medium,
colors = ButtonDefaults.buttonColors(
@@ -401,7 +366,6 @@ sealed class InstallMethod {
@Composable
private fun SelectInstallMethod(
isGKI: Boolean = false,
isAbDevice: Boolean = false,
onSelected: (InstallMethod) -> Unit = {}
) {
val rootAvailable = rootAvailable()
@@ -488,8 +452,8 @@ private fun SelectInstallMethod(
}
}
var LKMExpanded by remember { mutableStateOf(false) }
var GKIExpanded by remember { mutableStateOf(false) }
var lkmExpanded by remember { mutableStateOf(false) }
var gkiExpanded by remember { mutableStateOf(false) }
Column(
modifier = Modifier.padding(horizontal = 16.dp)
@@ -498,38 +462,39 @@ private fun SelectInstallMethod(
if (isGKI) {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation),
elevation = getCardElevation(),
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
.padding(bottom = 16.dp)
.clip(MaterialTheme.shapes.large)
.shadow(
elevation = cardElevation,
shape = MaterialTheme.shapes.large,
spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f)
)
) {
ListItem(
leadingContent = {
Icon(
Icons.Filled.AutoFixHigh,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
},
headlineContent = {
Text(
stringResource(R.string.Lkm_install_methods),
style = MaterialTheme.typography.titleMedium
)
},
modifier = Modifier.clickable {
LKMExpanded = !LKMExpanded
}
)
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else MaterialTheme.colorScheme.surfaceVariant
)
) {
ListItem(
leadingContent = {
Icon(
Icons.Filled.AutoFixHigh,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
},
headlineContent = {
Text(
stringResource(R.string.Lkm_install_methods),
style = MaterialTheme.typography.titleMedium
)
},
modifier = Modifier.clickable {
lkmExpanded = !lkmExpanded
}
)
}
AnimatedVisibility(
visible = LKMExpanded,
visible = lkmExpanded,
enter = fadeIn() + expandVertically(),
exit = shrinkVertically() + fadeOut()
) {
@@ -604,38 +569,39 @@ private fun SelectInstallMethod(
if (rootAvailable) {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation),
elevation = getCardElevation(),
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
.clip(MaterialTheme.shapes.large)
.shadow(
elevation = cardElevation,
shape = MaterialTheme.shapes.large,
spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f)
)
) {
ListItem(
leadingContent = {
Icon(
Icons.Filled.FileUpload,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
},
headlineContent = {
Text(
stringResource(R.string.GKI_install_methods),
style = MaterialTheme.typography.titleMedium
)
},
modifier = Modifier.clickable {
GKIExpanded = !GKIExpanded
}
)
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else MaterialTheme.colorScheme.surfaceVariant
)
) {
ListItem(
leadingContent = {
Icon(
Icons.Filled.FileUpload,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
},
headlineContent = {
Text(
stringResource(R.string.GKI_install_methods),
style = MaterialTheme.typography.titleMedium
)
},
modifier = Modifier.clickable {
gkiExpanded = !gkiExpanded
}
)
}
AnimatedVisibility(
visible = GKIExpanded,
visible = gkiExpanded,
enter = fadeIn() + expandVertically(),
exit = shrinkVertically() + fadeOut()
) {
@@ -647,60 +613,60 @@ private fun SelectInstallMethod(
)
) {
radioOptions.filterIsInstance<InstallMethod.HorizonKernel>().forEach { option ->
val interactionSource = remember { MutableInteractionSource() }
Surface(
color = if (option.javaClass == selectedOption?.javaClass)
MaterialTheme.colorScheme.secondaryContainer.copy(alpha = cardAlpha)
else
MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = cardAlpha),
shape = MaterialTheme.shapes.medium,
val interactionSource = remember { MutableInteractionSource() }
Surface(
color = if (option.javaClass == selectedOption?.javaClass)
MaterialTheme.colorScheme.secondaryContainer.copy(alpha = cardAlpha)
else
MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = cardAlpha),
shape = MaterialTheme.shapes.medium,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
.clip(MaterialTheme.shapes.medium)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
.clip(MaterialTheme.shapes.medium)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.toggleable(
value = option.javaClass == selectedOption?.javaClass,
onValueChange = { onClick(option) },
role = Role.RadioButton,
indication = LocalIndication.current,
interactionSource = interactionSource
)
.padding(vertical = 8.dp, horizontal = 12.dp)
) {
RadioButton(
selected = option.javaClass == selectedOption?.javaClass,
onClick = null,
interactionSource = interactionSource,
colors = RadioButtonDefaults.colors(
selectedColor = MaterialTheme.colorScheme.primary,
unselectedColor = MaterialTheme.colorScheme.onSurfaceVariant
)
.toggleable(
value = option.javaClass == selectedOption?.javaClass,
onValueChange = { onClick(option) },
role = Role.RadioButton,
indication = LocalIndication.current,
interactionSource = interactionSource
)
Column(
modifier = Modifier
.padding(start = 10.dp)
.weight(1f)
) {
.padding(vertical = 8.dp, horizontal = 12.dp)
) {
RadioButton(
selected = option.javaClass == selectedOption?.javaClass,
onClick = null,
interactionSource = interactionSource,
colors = RadioButtonDefaults.colors(
selectedColor = MaterialTheme.colorScheme.primary,
unselectedColor = MaterialTheme.colorScheme.onSurfaceVariant
)
)
Column(
modifier = Modifier
.padding(start = 10.dp)
.weight(1f)
) {
Text(
text = stringResource(id = option.label),
style = MaterialTheme.typography.bodyLarge
)
option.summary?.let {
Text(
text = stringResource(id = option.label),
style = MaterialTheme.typography.bodyLarge
text = it,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
option.summary?.let {
Text(
text = it,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
}
}
}
}
}
}
@@ -722,7 +688,7 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
}
var selection by remember { mutableStateOf<String?>(null) }
val backgroundColor = MaterialTheme.colorScheme.surfaceContainerHighest
val backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
@@ -749,10 +715,14 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
@Composable
private fun TopBar(
onBack: () -> Unit = {},
onLkmUpload: () -> Unit = {},
scrollBehavior: TopAppBarScrollBehavior? = null
) {
val cardColor = MaterialTheme.colorScheme.surfaceVariant
val colorScheme = MaterialTheme.colorScheme
val cardColor = if (CardConfig.isCustomBackgroundEnabled) {
colorScheme.surfaceContainerLow
} else {
colorScheme.background
}
val cardAlpha = cardAlpha
TopAppBar(

View File

@@ -112,7 +112,6 @@ fun KpmScreen(
Text(
text = kpmInstallMode,
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface
)
},
text = {
@@ -121,7 +120,6 @@ fun KpmScreen(
Text(
text = stringResource(R.string.kpm_install_mode_description, it),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Spacer(modifier = Modifier.height(16.dp))
@@ -146,9 +144,6 @@ fun KpmScreen(
}
},
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
)
) {
Icon(
imageVector = Icons.Filled.Download,
@@ -176,9 +171,6 @@ fun KpmScreen(
}
},
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondary
)
) {
Icon(
imageVector = Icons.Filled.Inventory,
@@ -209,7 +201,6 @@ fun KpmScreen(
}
}
},
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
shape = MaterialTheme.shapes.extraLarge
)
}
@@ -309,10 +300,8 @@ fun KpmScreen(
text = {
Text(
text = stringResource(R.string.kpm_install),
color = MaterialTheme.colorScheme.onPrimaryContainer
)
},
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
expanded = true,
)
},
@@ -321,9 +310,6 @@ fun KpmScreen(
Column(modifier = Modifier.padding(padding)) {
if (!isNoticeClosed) {
Card(
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer
),
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
@@ -348,7 +334,6 @@ fun KpmScreen(
text = stringResource(R.string.kernel_module_notice),
modifier = Modifier.weight(1f),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
IconButton(
@@ -357,9 +342,6 @@ fun KpmScreen(
sharedPreferences.edit { putBoolean("is_notice_closed", true) }
},
modifier = Modifier.size(24.dp),
colors = IconButtonDefaults.iconButtonColors(
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
)
) {
Icon(
imageVector = Icons.Filled.Close,
@@ -391,7 +373,6 @@ fun KpmScreen(
stringResource(R.string.kpm_empty),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@@ -585,7 +566,6 @@ private fun KpmModuleItem(
Text(
text = stringResource(R.string.kpm_control),
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface
)
},
text = {
@@ -595,20 +575,14 @@ private fun KpmModuleItem(
label = {
Text(
text = stringResource(R.string.kpm_args),
color = MaterialTheme.colorScheme.primary
)
},
placeholder = {
Text(
text = module.args,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f)
)
},
modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.outline
)
)
},
confirmButton = {
@@ -627,7 +601,6 @@ private fun KpmModuleItem(
) {
Text(
text = stringResource(R.string.confirm),
color = MaterialTheme.colorScheme.primary
)
}
},
@@ -635,26 +608,16 @@ private fun KpmModuleItem(
TextButton(onClick = { viewModel.hideInputDialog() }) {
Text(
text = stringResource(R.string.cancel),
color = MaterialTheme.colorScheme.primary
)
}
},
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
shape = MaterialTheme.shapes.extraLarge
)
}
Card(
colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation),
modifier = Modifier
.fillMaxWidth()
.clip(MaterialTheme.shapes.large)
.shadow(
elevation = cardElevation,
shape = MaterialTheme.shapes.large,
spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f)
)
elevation = getCardElevation()
) {
Column(
modifier = Modifier.padding(20.dp)
@@ -668,7 +631,6 @@ private fun KpmModuleItem(
Text(
text = module.name,
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.height(4.dp))
@@ -676,19 +638,16 @@ private fun KpmModuleItem(
Text(
text = "${stringResource(R.string.kpm_version)}: ${module.version}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = "${stringResource(R.string.kpm_author)}: ${module.author}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = "${stringResource(R.string.kpm_args)}: ${module.args}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@@ -698,7 +657,6 @@ private fun KpmModuleItem(
Text(
text = module.description,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(20.dp))
@@ -711,10 +669,6 @@ private fun KpmModuleItem(
onClick = { viewModel.showInputDialog(module.id) },
enabled = module.hasAction,
modifier = Modifier.weight(1f),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Icon(
imageVector = Icons.Filled.Settings,

View File

@@ -4,6 +4,7 @@ import android.app.Activity.*
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@@ -22,9 +23,7 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.*
import androidx.compose.ui.res.stringResource
@@ -64,18 +63,21 @@ import okhttp3.OkHttpClient
import com.sukisu.ultra.ui.util.ModuleModify
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.viewmodel.ModuleViewModel
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.concurrent.TimeUnit
import java.util.zip.ZipInputStream
import androidx.core.content.edit
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import com.sukisu.ultra.ui.webui.WebUIXActivity
import com.dergoogler.mmrl.platform.Platform
import androidx.core.net.toUri
import com.dergoogler.mmrl.platform.model.ModuleConfig
import com.dergoogler.mmrl.platform.model.ModuleConfig.Companion.asModuleConfig
import com.sukisu.ultra.ui.theme.getCardElevation
/**
* @author ShirkNeko
* @date 2025/5/31.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@Composable
@@ -85,6 +87,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
val snackBarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
val confirmDialog = rememberConfirmDialog()
var lastClickTime by remember { mutableStateOf(0L) }
val selectZipLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
@@ -97,38 +100,21 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
scope.launch {
val clipData = data.clipData
if (clipData != null) {
// 处理多选结果
val selectedModules = mutableListOf<Uri>()
val selectedModuleNames = mutableMapOf<Uri, String>()
suspend fun processUri(uri: Uri) {
val moduleName = withContext(Dispatchers.IO) {
try {
val zipInputStream = ZipInputStream(context.contentResolver.openInputStream(uri))
var entry = zipInputStream.nextEntry
var name = context.getString(R.string.unknown_module)
while (entry != null) {
if (entry.name == "module.prop") {
val reader = BufferedReader(InputStreamReader(zipInputStream))
var line: String?
while (reader.readLine().also { line = it } != null) {
if (line?.startsWith("name=") == true) {
name = line.substringAfter("=")
break
}
}
break
}
entry = zipInputStream.nextEntry
}
name
} catch (e: Exception) {
context.getString(R.string.unknown_module)
fun processUri(uri: Uri) {
try {
if (!ModuleUtils.isUriAccessible(context, uri)) {
return
}
ModuleUtils.takePersistableUriPermission(context, uri)
val moduleName = ModuleUtils.extractModuleName(context, uri)
selectedModules.add(uri)
selectedModuleNames[uri] = moduleName
} catch (e: Exception) {
Log.e("ModuleScreen", "Error while processing URI: $uri, Error: ${e.message}")
}
selectedModules.add(uri)
selectedModuleNames[uri] = moduleName
}
for (i in 0 until clipData.itemCount) {
@@ -136,7 +122,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
processUri(uri)
}
// 显示确认对话框
if (selectedModules.isEmpty()) {
snackBarHost.showSnackbar("Unable to access selected module files")
return@launch
}
val modulesList = selectedModuleNames.values.joinToString("\n", "")
val confirmResult = confirmDialog.awaitConfirm(
title = context.getString(R.string.module_install),
@@ -146,49 +136,42 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
)
if (confirmResult == ConfirmResult.Confirmed) {
// 批量安装模块
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(selectedModules)))
viewModel.markNeedRefresh()
}
} else {
// 单个文件安装逻辑
val uri = data.data ?: return@launch
val moduleName = withContext(Dispatchers.IO) {
try {
val zipInputStream = ZipInputStream(context.contentResolver.openInputStream(uri))
var entry = zipInputStream.nextEntry
var name = context.getString(R.string.unknown_module)
while (entry != null) {
if (entry.name == "module.prop") {
val reader = BufferedReader(InputStreamReader(zipInputStream))
var line: String?
while (reader.readLine().also { line = it } != null) {
if (line?.startsWith("name=") == true) {
name = line.substringAfter("=")
break
}
}
break
}
entry = zipInputStream.nextEntry
}
name
// 批量安装模块
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(selectedModules)))
viewModel.markNeedRefresh()
} catch (e: Exception) {
context.getString(R.string.unknown_module)
Log.e("ModuleScreen", "Error navigating to FlashScreen: ${e.message}")
snackBarHost.showSnackbar("Error while installing module: ${e.message}")
}
}
} else {
val uri = data.data ?: return@launch
// 单个安装模块
try {
if (!ModuleUtils.isUriAccessible(context, uri)) {
snackBarHost.showSnackbar("Unable to access selected module files")
return@launch
}
val confirmResult = confirmDialog.awaitConfirm(
title = context.getString(R.string.module_install),
content = context.getString(R.string.module_install_confirm, moduleName),
confirm = context.getString(R.string.install),
dismiss = context.getString(R.string.cancel)
)
ModuleUtils.takePersistableUriPermission(context, uri)
if (confirmResult == ConfirmResult.Confirmed) {
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri)))
viewModel.markNeedRefresh()
val moduleName = ModuleUtils.extractModuleName(context, uri)
val confirmResult = confirmDialog.awaitConfirm(
title = context.getString(R.string.module_install),
content = context.getString(R.string.module_install_confirm, moduleName),
confirm = context.getString(R.string.install),
dismiss = context.getString(R.string.cancel)
)
if (confirmResult == ConfirmResult.Confirmed) {
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri)))
viewModel.markNeedRefresh()
}
} catch (e: Exception) {
Log.e("ModuleScreen", "Error processing a single URI: $uri, Error: ${e.message}")
snackBarHost.showSnackbar("Error processing module file: ${e.message}")
}
}
}
@@ -246,10 +229,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
Checkbox(
checked = viewModel.sortActionFirst,
onCheckedChange = null,
colors = CheckboxDefaults.colors(
checkedColor = MaterialTheme.colorScheme.primary,
uncheckedColor = MaterialTheme.colorScheme.outline
)
)
},
onClick = {
@@ -271,10 +250,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
Checkbox(
checked = viewModel.sortEnabledFirst,
onCheckedChange = null,
colors = CheckboxDefaults.colors(
checkedColor = MaterialTheme.colorScheme.primary,
uncheckedColor = MaterialTheme.colorScheme.outline
)
)
},
onClick = {
@@ -292,7 +267,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
text = { Text(stringResource(R.string.backup_modules)) },
leadingIcon = {
Icon(
imageVector = Icons.Outlined.Download,
imageVector = Icons.Outlined.Save,
contentDescription = stringResource(R.string.backup),
)
},
@@ -305,7 +280,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
text = { Text(stringResource(R.string.restore_modules)) },
leadingIcon = {
Icon(
imageVector = Icons.Outlined.Refresh,
imageVector = Icons.Outlined.RestoreFromTrash,
contentDescription = stringResource(R.string.restore),
)
},
@@ -341,10 +316,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
text = {
Text(
text = moduleInstall,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
},
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
expanded = true,
)
}
@@ -369,7 +342,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
Icon(
imageVector = Icons.Outlined.Warning,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
modifier = Modifier
.size(64.dp)
.padding(bottom = 16.dp)
@@ -378,7 +350,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
stringResource(R.string.module_magisk_conflict),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@@ -393,20 +364,60 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it)))
},
onClickModule = { id, name, hasWebUi ->
val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime < 600) {
Log.d("ModuleScreen", "Click too fast, ignoring")
return@ModuleList
}
lastClickTime = currentTime
if (hasWebUi) {
webUILauncher.launch(
if (prefs.getBoolean("use_webuix", false) && Platform.isAlive) {
Intent(context, WebUIXActivity::class.java)
.setData("kernelsu://webuix/$id".toUri())
.putExtra("id", id)
.putExtra("name", name)
} else {
Intent(context, WebUIActivity::class.java)
.setData("kernelsu://webui/$id".toUri())
.putExtra("id", id)
.putExtra("name", name)
try {
val wxEngine = Intent(context, WebUIXActivity::class.java)
.setData("kernelsu://webuix/$id".toUri())
.putExtra("id", id)
.putExtra("name", name)
val ksuEngine = Intent(context, WebUIActivity::class.java)
.setData("kernelsu://webui/$id".toUri())
.putExtra("id", id)
.putExtra("name", name)
val config = try {
id.asModuleConfig
} catch (e: Exception) {
Log.e("ModuleScreen", "Failed to get config from id: $id", e)
null
}
)
val globalEngine = prefs.getString("webui_engine", "default") ?: "default"
val moduleEngine = config?.getWebuiEngine(context)
val selectedEngine = when (globalEngine) {
"wx" -> wxEngine
"ksu" -> ksuEngine
"default" -> {
when (moduleEngine) {
"wx" -> wxEngine
"ksu" -> ksuEngine
else -> {
if (Platform.isAlive) {
wxEngine
} else {
ksuEngine
}
}
}
}
else -> ksuEngine
}
webUILauncher.launch(selectedEngine)
} catch (e: Exception) {
Log.e("ModuleScreen", "Error launching WebUI: ${e.message}", e)
scope.launch {
snackBarHost.showSnackbar("Error launching WebUI: ${e.message}")
}
}
return@ModuleList
}
},
context = context,
@@ -616,7 +627,6 @@ private fun ModuleList(
text = stringResource(R.string.module_empty),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@@ -702,15 +712,7 @@ fun ModuleItem(
) {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation),
modifier = Modifier
.fillMaxWidth()
.clip(MaterialTheme.shapes.large)
.shadow(
elevation = cardElevation,
shape = MaterialTheme.shapes.large,
spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f)
)
elevation = getCardElevation(),
) {
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
val interactionSource = remember { MutableInteractionSource() }
@@ -753,7 +755,6 @@ fun ModuleItem(
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
textDecoration = textDecoration,
color = MaterialTheme.colorScheme.onSurface
)
Text(
@@ -762,7 +763,6 @@ fun ModuleItem(
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
textDecoration = textDecoration,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
@@ -771,7 +771,6 @@ fun ModuleItem(
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
textDecoration = textDecoration,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
@@ -786,14 +785,6 @@ fun ModuleItem(
checked = module.enabled,
onCheckedChange = onCheckChanged,
interactionSource = if (!module.hasWebUi) interactionSource else null,
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colorScheme.onPrimary,
checkedTrackColor = MaterialTheme.colorScheme.primary,
checkedIconColor = MaterialTheme.colorScheme.primary,
uncheckedThumbColor = MaterialTheme.colorScheme.outline,
uncheckedTrackColor = MaterialTheme.colorScheme.surfaceVariant,
uncheckedIconColor = MaterialTheme.colorScheme.surfaceVariant
)
)
}
}
@@ -809,7 +800,6 @@ fun ModuleItem(
overflow = TextOverflow.Ellipsis,
maxLines = 4,
textDecoration = textDecoration,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(16.dp))
@@ -831,7 +821,6 @@ fun ModuleItem(
viewModel.markNeedRefresh()
},
contentPadding = ButtonDefaults.TextButtonContentPadding,
colors = ButtonDefaults.filledTonalButtonColors()
) {
Icon(
modifier = Modifier.size(20.dp),
@@ -848,7 +837,6 @@ fun ModuleItem(
onClick = { onClick(module) },
interactionSource = interactionSource,
contentPadding = ButtonDefaults.TextButtonContentPadding,
colors = ButtonDefaults.filledTonalButtonColors()
) {
Icon(
@@ -881,8 +869,6 @@ fun ModuleItem(
modifier = Modifier.defaultMinSize(minWidth = 52.dp, minHeight = 32.dp),
onClick = { onUninstallClicked(module) },
contentPadding = ButtonDefaults.TextButtonContentPadding,
colors = ButtonDefaults.filledTonalButtonColors(
containerColor = if (!module.remove) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.errorContainer)
) {
if (!module.remove) {
Icon(
@@ -919,7 +905,8 @@ fun ModuleItemPreview() {
updateJson = "",
hasWebUi = false,
hasActionScript = false,
dirId = "dirId"
dirId = "dirId",
config = ModuleConfig(),
)
ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {})
}

View File

@@ -12,9 +12,7 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
@@ -55,18 +53,22 @@ import kotlinx.coroutines.withContext
import com.sukisu.ultra.BuildConfig
import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.*
import com.sukisu.ultra.ui.component.*
import com.sukisu.ultra.ui.theme.*
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.getBugreportFile
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import com.sukisu.ultra.ui.component.KsuIsValid
import com.dergoogler.mmrl.platform.Platform
/**
* @author ShirkNeko
* @date 2025/5/31.
*/
private val SPACING_SMALL = 3.dp
private val SPACING_MEDIUM = 8.dp
private val SPACING_LARGE = 16.dp
@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@@ -74,12 +76,18 @@ import com.dergoogler.mmrl.platform.Platform
fun SettingScreen(navigator: DestinationsNavigator) {
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val snackBarHost = LocalSnackbarHost.current
val context = LocalContext.current
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
var selectedEngine by rememberSaveable {
mutableStateOf(
prefs.getString("webui_engine", "default") ?: "default"
)
}
Scaffold(
// containerColor = MaterialTheme.colorScheme.surfaceBright,
topBar = {
TopBar(
scrollBehavior = scrollBehavior
)
TopBar(scrollBehavior = scrollBehavior)
},
snackbarHost = { SnackbarHost(snackBarHost) },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
@@ -113,31 +121,16 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
}
// 配置
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLow.copy(alpha = cardAlpha)
),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation)
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = stringResource(R.string.configuration),
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
)
// 配置卡片
SettingsGroupCard(
title = stringResource(R.string.configuration),
content = {
// 配置文件模板入口
val profileTemplate = stringResource(id = R.string.settings_profile_template)
KsuIsValid {
SettingItem(
icon = Icons.Filled.Fence,
title = profileTemplate,
summary = stringResource(id = R.string.settings_profile_template_summary),
title = stringResource(R.string.settings_profile_template),
summary = stringResource(R.string.settings_profile_template_summary),
onClick = {
navigator.navigate(AppProfileTemplateScreenDestination)
}
@@ -150,244 +143,176 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
KsuIsValid {
SwitchSettingItem(
SwitchItem(
icon = Icons.Filled.FolderDelete,
title = stringResource(id = R.string.settings_umount_modules_default),
summary = stringResource(id = R.string.settings_umount_modules_default_summary),
title = stringResource(R.string.settings_umount_modules_default),
summary = stringResource(R.string.settings_umount_modules_default_summary),
checked = umountChecked,
onCheckedChange = {
if (Natives.setDefaultUmountModules(it)) {
umountChecked = it
onCheckedChange = { enabled ->
if (Natives.setDefaultUmountModules(enabled)) {
umountChecked = enabled
}
}
)
}
// SU 禁用开关(仅在兼容版本显示)
// SU 禁用开关
KsuIsValid {
if (Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT) {
var isSuDisabled by rememberSaveable {
mutableStateOf(!Natives.isSuEnabled())
}
SwitchSettingItem(
SwitchItem(
icon = Icons.Filled.RemoveModerator,
title = stringResource(id = R.string.settings_disable_su),
summary = stringResource(id = R.string.settings_disable_su_summary),
title = stringResource(R.string.settings_disable_su),
summary = stringResource(R.string.settings_disable_su_summary),
checked = isSuDisabled,
onCheckedChange = { checked ->
val shouldEnable = !checked
onCheckedChange = { enabled ->
val shouldEnable = !enabled
if (Natives.setSuEnabled(shouldEnable)) {
isSuDisabled = !shouldEnable
isSuDisabled = enabled
}
}
)
}
}
}
}
// 应用设置
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLow.copy(alpha = cardAlpha)
),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation)
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = stringResource(R.string.app_settings),
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
)
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
)
// 应用设置卡片
SettingsGroupCard(
title = stringResource(R.string.app_settings),
content = {
// 更新检查开关
var checkUpdate by rememberSaveable {
mutableStateOf(
prefs.getBoolean("check_update", true)
)
mutableStateOf(prefs.getBoolean("check_update", true))
}
SwitchSettingItem(
SwitchItem(
icon = Icons.Filled.Update,
title = stringResource(id = R.string.settings_check_update),
summary = stringResource(id = R.string.settings_check_update_summary),
title = stringResource(R.string.settings_check_update),
summary = stringResource(R.string.settings_check_update_summary),
checked = checkUpdate,
onCheckedChange = {
prefs.edit {putBoolean("check_update", it) }
checkUpdate = it
onCheckedChange = { enabled ->
prefs.edit { putBoolean("check_update", enabled) }
checkUpdate = enabled
}
)
// Web调试开关
var enableWebDebugging by rememberSaveable {
mutableStateOf(
prefs.getBoolean("enable_web_debugging", false)
)
}
// WebUI引擎选择
KsuIsValid {
SwitchSettingItem(
icon = Icons.Filled.DeveloperMode,
title = stringResource(id = R.string.enable_web_debugging),
summary = stringResource(id = R.string.enable_web_debugging_summary),
checked = enableWebDebugging,
onCheckedChange = {
prefs.edit { putBoolean("enable_web_debugging", it) }
enableWebDebugging = it
WebUIEngineSelector(
selectedEngine = selectedEngine,
onEngineSelected = { engine ->
selectedEngine = engine
prefs.edit { putString("webui_engine", engine) }
}
)
}
// Web X 开关
var useWebUIX by rememberSaveable {
mutableStateOf(
prefs.getBoolean("use_webuix", false)
)
// Web调试和Web X Eruda 开关
var enableWebDebugging by rememberSaveable {
mutableStateOf(prefs.getBoolean("enable_web_debugging", false))
}
KsuIsValid {
SwitchItem(
beta = true,
enabled = Platform.isAlive,
icon = Icons.Filled.WebAsset,
title = stringResource(id = R.string.use_webuix),
summary = stringResource(id = R.string.use_webuix_summary),
checked = useWebUIX
) {
prefs.edit { putBoolean("use_webuix", it) }
useWebUIX = it
}
var useWebUIXEruda by rememberSaveable {
mutableStateOf(prefs.getBoolean("use_webuix_eruda", false))
}
// Web X Eruda 开关
var useWebUIXEruda by rememberSaveable {
mutableStateOf(
prefs.getBoolean("use_webuix_eruda", false)
)
}
KsuIsValid {
SwitchItem(
icon = Icons.Filled.DeveloperMode,
title = stringResource(R.string.enable_web_debugging),
summary = stringResource(R.string.enable_web_debugging_summary),
checked = enableWebDebugging,
onCheckedChange = { enabled ->
prefs.edit { putBoolean("enable_web_debugging", enabled) }
enableWebDebugging = enabled
}
)
AnimatedVisibility(
visible = useWebUIX && enableWebDebugging,
visible = enableWebDebugging && selectedEngine == "wx",
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
SwitchItem(
beta = true,
enabled = Platform.isAlive && useWebUIX && enableWebDebugging,
icon = Icons.Filled.FormatListNumbered,
title = stringResource(id = R.string.use_webuix_eruda),
summary = stringResource(id = R.string.use_webuix_eruda_summary),
checked = useWebUIXEruda
) {
prefs.edit { putBoolean("use_webuix_eruda", it) }
useWebUIXEruda = it
}
title = stringResource(R.string.use_webuix_eruda),
summary = stringResource(R.string.use_webuix_eruda_summary),
checked = useWebUIXEruda,
onCheckedChange = { enabled ->
prefs.edit { putBoolean("use_webuix_eruda", enabled) }
useWebUIXEruda = enabled
}
)
}
}
// 更多设置
SettingItem(
icon = Icons.Filled.Settings,
title = stringResource(id = R.string.more_settings),
summary = stringResource(id = R.string.more_settings),
title = stringResource(R.string.more_settings),
summary = stringResource(R.string.more_settings),
onClick = {
navigator.navigate(MoreSettingsScreenDestination)
}
)
}
}
// 工具
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLow.copy(alpha = cardAlpha)
),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation)
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = stringResource(R.string.tools),
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
)
)
// 工具卡片
SettingsGroupCard(
title = stringResource(R.string.tools),
content = {
var showBottomsheet by remember { mutableStateOf(false) }
SettingItem(
icon = Icons.Filled.BugReport,
title = stringResource(id = R.string.send_log),
title = stringResource(R.string.send_log),
onClick = {
showBottomsheet = true
}
)
if (showBottomsheet) {
ModalBottomSheet(
onDismissRequest = { showBottomsheet = false },
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
LogActionButton(
icon = Icons.Filled.Save,
text = stringResource(R.string.save_log),
onClick = {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
val current = LocalDateTime.now().format(formatter)
exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz")
showBottomsheet = false
}
)
LogActionButton(
icon = Icons.Filled.Share,
text = stringResource(R.string.send_log),
onClick = {
scope.launch {
val bugreport = loadingDialog.withLoading {
withContext(Dispatchers.IO) {
getBugreportFile(context)
}
}
val uri: Uri =
FileProvider.getUriForFile(
context,
"${BuildConfig.APPLICATION_ID}.fileprovider",
bugreport
)
val shareIntent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_STREAM, uri)
setDataAndType(uri, "application/gzip")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(
Intent.createChooser(
shareIntent,
context.getString(R.string.send_log)
)
)
showBottomsheet = false
LogBottomSheet(
onDismiss = { showBottomsheet = false },
onSaveLog = {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
val current = LocalDateTime.now().format(formatter)
exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz")
showBottomsheet = false
},
onShareLog = {
scope.launch {
val bugreport = loadingDialog.withLoading {
withContext(Dispatchers.IO) {
getBugreportFile(context)
}
}
)
val uri = FileProvider.getUriForFile(
context,
"${BuildConfig.APPLICATION_ID}.fileprovider",
bugreport
)
val shareIntent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_STREAM, uri)
setDataAndType(uri, "application/gzip")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(
Intent.createChooser(
shareIntent,
context.getString(R.string.send_log)
)
)
showBottomsheet = false
}
}
Spacer(modifier = Modifier.height(16.dp))
}
)
}
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
@@ -397,26 +322,12 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
}
}
}
// 设置分组卡片 - 关于
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLow.copy(alpha = cardAlpha)
),
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation)
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = stringResource(R.string.about),
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
)
)
// 关于卡片
SettingsGroupCard(
title = stringResource(R.string.about),
content = {
SettingItem(
icon = Icons.Filled.Info,
title = stringResource(R.string.about),
@@ -425,13 +336,128 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
)
}
}
)
Spacer(modifier = Modifier.height(16.dp))
Spacer(modifier = Modifier.height(SPACING_LARGE))
}
}
}
@Composable
private fun SettingsGroupCard(
title: String,
content: @Composable ColumnScope.() -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = SPACING_LARGE, vertical = SPACING_MEDIUM),
colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerLow),
elevation = getCardElevation()
) {
Column(
modifier = Modifier.padding(vertical = SPACING_MEDIUM)
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = SPACING_LARGE, vertical = SPACING_MEDIUM),
color = MaterialTheme.colorScheme.primary
)
content()
}
}
}
@Composable
private fun WebUIEngineSelector(
selectedEngine: String,
onEngineSelected: (String) -> Unit
) {
var showDialog by remember { mutableStateOf(false) }
val engineOptions = listOf(
"default" to stringResource(R.string.engine_auto_select),
"wx" to stringResource(R.string.engine_force_webuix),
"ksu" to stringResource(R.string.engine_force_ksu)
)
SettingItem(
icon = Icons.Filled.WebAsset,
title = stringResource(R.string.use_webuix),
summary = engineOptions.find { it.first == selectedEngine }?.second
?: stringResource(R.string.engine_auto_select),
onClick = { showDialog = true }
)
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text(stringResource(R.string.use_webuix)) },
text = {
Column {
engineOptions.forEach { (value, label) ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
onEngineSelected(value)
showDialog = false
}
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = selectedEngine == value,
onClick = null
)
Spacer(modifier = Modifier.width(SPACING_MEDIUM))
Text(text = label)
}
}
}
},
confirmButton = {
TextButton(onClick = { showDialog = false }) {
Text(stringResource(R.string.cancel))
}
}
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LogBottomSheet(
onDismiss: () -> Unit,
onSaveLog: () -> Unit,
onShareLog: () -> Unit
) {
ModalBottomSheet(
onDismissRequest = onDismiss,
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(SPACING_LARGE),
horizontalArrangement = Arrangement.SpaceEvenly
) {
LogActionButton(
icon = Icons.Filled.Save,
text = stringResource(R.string.save_log),
onClick = onSaveLog
)
LogActionButton(
icon = Icons.Filled.Share,
text = stringResource(R.string.send_log),
onClick = onShareLog
)
}
Spacer(modifier = Modifier.height(SPACING_LARGE))
}
}
@Composable
fun LogActionButton(
icon: ImageVector,
@@ -442,7 +468,7 @@ fun LogActionButton(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.clickable(onClick = onClick)
.padding(8.dp)
.padding(SPACING_MEDIUM)
) {
Box(
contentAlignment = Alignment.Center,
@@ -458,11 +484,10 @@ fun LogActionButton(
modifier = Modifier.size(24.dp)
)
}
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(SPACING_MEDIUM))
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface
style = MaterialTheme.typography.bodyMedium
)
}
}
@@ -478,33 +503,31 @@ fun SettingItem(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
.padding(horizontal = SPACING_LARGE, vertical = 12.dp),
verticalAlignment = Alignment.Top
) {
Icon(
imageVector = icon,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier
.padding(end = 16.dp)
.padding(end = SPACING_LARGE)
.size(24.dp)
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface
style = MaterialTheme.typography.titleMedium
)
if (summary != null) {
Spacer(modifier = Modifier.height(SPACING_SMALL))
Text(
text = summary,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
style = MaterialTheme.typography.bodyMedium
)
}
}
Icon(
imageVector = Icons.Filled.ChevronRight,
contentDescription = null,
@@ -515,7 +538,7 @@ fun SettingItem(
}
@Composable
fun SwitchSettingItem(
fun SwitchItem(
icon: ImageVector,
title: String,
summary: String? = null,
@@ -526,44 +549,34 @@ fun SwitchSettingItem(
modifier = Modifier
.fillMaxWidth()
.clickable { onCheckedChange(!checked) }
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
.padding(horizontal = SPACING_LARGE, vertical = 12.dp),
verticalAlignment = Alignment.Top
) {
Icon(
imageVector = icon,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
tint = if (checked) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier
.padding(end = 16.dp)
.padding(end = SPACING_LARGE)
.size(24.dp)
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface
style = MaterialTheme.typography.titleMedium
)
if (summary != null) {
Spacer(modifier = Modifier.height(SPACING_SMALL))
Text(
text = summary,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
style = MaterialTheme.typography.bodyMedium
)
}
}
Switch(
checked = checked,
onCheckedChange = onCheckedChange,
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colorScheme.onPrimary,
checkedTrackColor = MaterialTheme.colorScheme.primary,
checkedIconColor = MaterialTheme.colorScheme.primary,
uncheckedThumbColor = MaterialTheme.colorScheme.outline,
uncheckedTrackColor = MaterialTheme.colorScheme.surfaceVariant,
uncheckedIconColor = MaterialTheme.colorScheme.surfaceVariant
)
onCheckedChange = onCheckedChange
)
}
}
@@ -648,11 +661,6 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
}
var selectedOption by remember { mutableStateOf<UninstallType?>(null) }
val cardColor = if (!ThemeConfig.useDynamicColor) {
ThemeConfig.currentTheme.ButtonContrast
} else {
MaterialTheme.colorScheme.surfaceContainerHigh
}
AlertDialog(
onDismissRequest = {
@@ -662,7 +670,6 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
Text(
text = stringResource(R.string.settings_uninstall),
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface
)
},
text = {
@@ -676,10 +683,6 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
MaterialTheme.colorScheme.primaryContainer
else
Color.Transparent
val borderColor = if (isSelected)
MaterialTheme.colorScheme.primary
else
Color.Transparent
val contentColor = if (isSelected)
MaterialTheme.colorScheme.onPrimaryContainer
else
@@ -690,11 +693,6 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
.fillMaxWidth()
.clip(MaterialTheme.shapes.medium)
.background(backgroundColor)
.border(
width = 1.dp,
color = borderColor,
shape = MaterialTheme.shapes.medium
)
.clickable {
selectedOption = option
}
@@ -704,10 +702,7 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
Icon(
imageVector = option.icon,
contentDescription = null,
tint = if (isSelected)
MaterialTheme.colorScheme.primary
else
MaterialTheme.colorScheme.primary,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier
.padding(end = 16.dp)
.size(24.dp)
@@ -718,7 +713,6 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
Text(
text = listOptions[index].titleText,
style = MaterialTheme.typography.titleMedium,
color = contentColor
)
listOptions[index].subtitleText?.let {
Text(
@@ -757,12 +751,6 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
dismiss()
},
enabled = selectedOption != null,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary,
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
disabledContentColor = MaterialTheme.colorScheme.onSurfaceVariant
)
) {
Text(
text = stringResource(android.R.string.ok)
@@ -777,11 +765,9 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
) {
Text(
text = stringResource(android.R.string.cancel),
color = MaterialTheme.colorScheme.primary
)
}
},
containerColor = cardColor,
shape = MaterialTheme.shapes.extraLarge,
tonalElevation = 4.dp
)
@@ -793,14 +779,12 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
private fun TopBar(
scrollBehavior: TopAppBarScrollBehavior? = null
) {
val systemIsDark = isSystemInDarkTheme()
val cardColor = MaterialTheme.colorScheme.surfaceVariant
val cardAlpha = if (ThemeConfig.customBackgroundUri != null) {
cardAlpha
val colorScheme = MaterialTheme.colorScheme
val cardColor = if (CardConfig.isCustomBackgroundEnabled) {
colorScheme.surfaceContainerLow
} else {
if (systemIsDark) 0.8f else 1f
colorScheme.background
}
TopAppBar(
title = {
Text(

View File

@@ -1,7 +1,8 @@
package com.sukisu.ultra.ui.screen
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
@@ -20,14 +21,12 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -44,7 +43,15 @@ import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.component.SearchAppBar
import com.sukisu.ultra.ui.util.ModuleModify
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.dergoogler.mmrl.ui.component.LabelItem
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation
/**
* @author ShirkNeko
* @date 2025/5/31.
*/
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@Composable
@@ -63,7 +70,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
LaunchedEffect(key1 = navigator) {
viewModel.search = ""
if (viewModel.appList.isEmpty()) {
viewModel.fetchAppList()
// viewModel.fetchAppList()
}
}
@@ -128,7 +135,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
)
},
onClick = {
viewModel.showSystemApps = !viewModel.showSystemApps
viewModel.updateShowSystemApps(!viewModel.showSystemApps)
showDropdown = false
}
)
@@ -186,11 +193,10 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
viewModel.showBatchActions = false
},
modifier = Modifier.size(if (isCancelPressed) 56.dp else 40.dp),
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
shape = CircleShape,
interactionSource = cancelInteractionSource,
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp),
containerColor = Color.Gray
) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -226,11 +232,10 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}
},
modifier = Modifier.size(if (isUnauthorizePressed) 56.dp else 40.dp),
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.onErrorContainer,
shape = CircleShape,
interactionSource = unauthorizeInteractionSource,
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp),
containerColor = MaterialTheme.colorScheme.error
) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -266,8 +271,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}
},
modifier = Modifier.size(if (isAuthorizePressed) 56.dp else 40.dp),
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
shape = CircleShape,
interactionSource = authorizeInteractionSource,
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
@@ -316,8 +319,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}
},
modifier = Modifier.size(if (isUmountEnablePressed) 56.dp else 40.dp),
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
shape = CircleShape,
interactionSource = umountEnableInteractionSource,
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
@@ -359,11 +360,10 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}
},
modifier = Modifier.size(if (isUmountDisablePressed) 56.dp else 40.dp),
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
shape = CircleShape,
interactionSource = umountDisableInteractionSource,
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp),
containerColor = MaterialTheme.colorScheme.error
) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -402,8 +402,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}
},
modifier = Modifier.size(if (isTopBtnPressed) 56.dp else 40.dp),
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 1f),
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
shape = CircleShape,
interactionSource = topBtnInteractionSource,
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
@@ -445,8 +443,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}
},
modifier = Modifier.size(if (isBottomBtnPressed) 56.dp else 40.dp),
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 1f),
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
shape = CircleShape,
interactionSource = bottomBtnInteractionSource,
elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
@@ -489,9 +485,12 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
.fillMaxSize()
.nestedScroll(scrollBehavior.nestedScrollConnection),
contentPadding = PaddingValues(
top = 8.dp,
start = 16.dp,
end = 16.dp,
top = 16.dp,
bottom = 16.dp
)
),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// 获取分组后的应用列表
val rootApps = viewModel.appList.filter { it.allowSu }
@@ -500,10 +499,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
// 显示ROOT权限应用组
if (rootApps.isNotEmpty()) {
item {
GroupHeader(title = stringResource(R.string.apps_with_root))
}
items(rootApps, key = { "root_" + it.packageName + it.uid }) { app ->
AppItem(
app = app,
@@ -514,9 +509,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
val profile = Natives.getAppProfile(app.packageName, app.uid)
val updatedProfile = profile.copy(allowSu = allowSu)
if (Natives.setAppProfile(updatedProfile)) {
// 不重新获取应用列表,避免滚动位置重置
// viewModel.fetchAppList()
// 仅更新当前应用的配置
viewModel.updateAppProfileLocally(app.packageName, updatedProfile)
}
}
@@ -542,10 +534,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
// 显示自定义配置应用组
if (customApps.isNotEmpty()) {
item {
GroupHeader(title = stringResource(R.string.apps_with_custom_profile))
}
items(customApps, key = { "custom_" + it.packageName + it.uid }) { app ->
AppItem(
app = app,
@@ -556,9 +544,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
val profile = Natives.getAppProfile(app.packageName, app.uid)
val updatedProfile = profile.copy(allowSu = allowSu)
if (Natives.setAppProfile(updatedProfile)) {
// 不重新获取应用列表,避免滚动位置重置
// viewModel.fetchAppList()
// 仅更新当前应用的配置
viewModel.updateAppProfileLocally(app.packageName, updatedProfile)
}
}
@@ -584,10 +569,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
// 显示其他应用组
if (otherApps.isNotEmpty()) {
item {
GroupHeader(title = stringResource(R.string.other_apps))
}
items(otherApps, key = { "other_" + it.packageName + it.uid }) { app ->
AppItem(
app = app,
@@ -598,9 +579,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
val profile = Natives.getAppProfile(app.packageName, app.uid)
val updatedProfile = profile.copy(allowSu = allowSu)
if (Natives.setAppProfile(updatedProfile)) {
// 不重新获取应用列表,避免滚动位置重置
// viewModel.fetchAppList()
// 仅更新当前应用的配置
viewModel.updateAppProfileLocally(app.packageName, updatedProfile)
}
}
@@ -638,7 +616,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
verticalArrangement = Arrangement.Center
) {
Icon(
imageVector = Icons.Filled.Apps,
imageVector = Icons.Filled.Archive,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f),
modifier = Modifier
@@ -649,7 +627,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
text = stringResource(R.string.no_apps_found),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@@ -660,26 +637,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}
}
@Composable
fun GroupHeader(title: String) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = 0.7f))
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
Text(
text = title,
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
)
}
}
@OptIn(ExperimentalLayoutApi::class)
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable
private fun AppItem(
app: SuperUserViewModel.AppInfo,
@@ -690,35 +648,10 @@ private fun AppItem(
onLongClick: () -> Unit,
viewModel: SuperUserViewModel
) {
val cardColor = if (app.allowSu)
MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.3f)
else if (app.hasCustomProfile)
MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.3f)
else
MaterialTheme.colorScheme.surfaceContainerLow
Card(
colors = CardDefaults.cardColors(containerColor = cardColor),
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp),
colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh),
elevation = getCardElevation(),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 4.dp)
.clip(MaterialTheme.shapes.medium)
.shadow(
elevation = 0.dp,
shape = MaterialTheme.shapes.medium,
spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f)
)
.then(
if (isSelected)
Modifier.border(
width = 2.dp,
color = MaterialTheme.colorScheme.primary,
shape = MaterialTheme.shapes.medium
)
else
Modifier
)
.pointerInput(Unit) {
detectTapGestures(
onLongPress = { onLongClick() },
@@ -752,17 +685,13 @@ private fun AppItem(
Text(
text = app.label,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
Text(
text = app.packageName,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
FlowRow(
@@ -770,13 +699,13 @@ private fun AppItem(
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
if (app.allowSu) {
LabelText(label = "ROOT", backgroundColor = MaterialTheme.colorScheme.primary)
LabelItem(text = "ROOT")
}
if (Natives.uidShouldUmount(app.uid)) {
LabelText(label = "UMOUNT", backgroundColor = MaterialTheme.colorScheme.tertiary)
LabelItem(text = "UNMOUNT")
}
if (app.hasCustomProfile) {
LabelText(label = "CUSTOM", backgroundColor = MaterialTheme.colorScheme.secondary)
LabelItem(text = "CUSTOM")
}
}
}
@@ -798,7 +727,6 @@ private fun AppItem(
Text(
text = if (app.allowSu) stringResource(R.string.authorized) else stringResource(R.string.unauthorized),
style = MaterialTheme.typography.labelMedium,
color = if (app.allowSu) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outline,
modifier = Modifier.padding(end = 4.dp)
)
}
@@ -807,18 +735,9 @@ private fun AppItem(
checked = app.allowSu,
onCheckedChange = onSwitchChange,
interactionSource = switchInteractionSource,
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colorScheme.onPrimary,
checkedTrackColor = MaterialTheme.colorScheme.primary,
checkedIconColor = MaterialTheme.colorScheme.primary,
uncheckedThumbColor = MaterialTheme.colorScheme.outline,
uncheckedTrackColor = MaterialTheme.colorScheme.surfaceVariant,
uncheckedIconColor = MaterialTheme.colorScheme.surfaceVariant
)
)
}
} else {
// 复选框交互源
val checkboxInteractionSource = remember { MutableInteractionSource() }
val isCheckboxPressed by checkboxInteractionSource.collectIsPressedAsState()
@@ -834,19 +753,13 @@ private fun AppItem(
Text(
text = if (isSelected) stringResource(R.string.selected) else stringResource(R.string.select),
style = MaterialTheme.typography.labelMedium,
color = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outline,
modifier = Modifier.padding(end = 4.dp)
)
}
Checkbox(
checked = isSelected,
onCheckedChange = { onToggleSelection() },
interactionSource = checkboxInteractionSource,
colors = CheckboxDefaults.colors(
checkedColor = MaterialTheme.colorScheme.primary,
uncheckedColor = MaterialTheme.colorScheme.outline
)
)
}
}
@@ -855,12 +768,12 @@ private fun AppItem(
}
@Composable
fun LabelText(label: String, backgroundColor: Color) {
fun LabelText(label: String) {
Box(
modifier = Modifier
.padding(top = 2.dp, end = 2.dp)
.background(
backgroundColor,
Color.Black,
shape = RoundedCornerShape(4.dp)
)
.clip(RoundedCornerShape(4.dp))
@@ -869,9 +782,8 @@ fun LabelText(label: String, backgroundColor: Color) {
text = label,
modifier = Modifier.padding(vertical = 2.dp, horizontal = 6.dp),
style = TextStyle(
fontSize = 10.sp,
fontSize = 8.sp,
color = Color.White,
fontWeight = FontWeight.Medium
)
)
}

View File

@@ -1,6 +1,5 @@
package com.sukisu.ultra.ui.screen
import LabelText
import android.content.ClipData
import android.content.ClipboardManager
import android.widget.Toast
@@ -208,13 +207,13 @@ private fun TemplateItem(
)
Text(template.description)
FlowRow {
LabelText(label = "UID: ${template.uid}", backgroundColor = MaterialTheme.colorScheme.surface)
LabelText(label = "GID: ${template.gid}", backgroundColor = MaterialTheme.colorScheme.surface)
LabelText(label = template.context, backgroundColor = MaterialTheme.colorScheme.surface)
LabelText(label = "UID: ${template.uid}")
LabelText(label = "GID: ${template.gid}")
LabelText(label = template.context,)
if (template.local) {
LabelText(label = "local", backgroundColor = MaterialTheme.colorScheme.surface)
LabelText(label = "local")
} else {
LabelText(label = "remote", backgroundColor = MaterialTheme.colorScheme.surface)
LabelText(label = "remote")
}
}
}
@@ -232,7 +231,12 @@ private fun TopBar(
colors: TopAppBarColors,
scrollBehavior: TopAppBarScrollBehavior? = null
) {
val cardColor = MaterialTheme.colorScheme.surfaceVariant
val colorScheme = MaterialTheme.colorScheme
val cardColor = if (CardConfig.isCustomBackgroundEnabled) {
colorScheme.surfaceContainerLow
} else {
colorScheme.background
}
val cardAlpha = CardConfig.cardAlpha
TopAppBar(

View File

@@ -10,19 +10,15 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
object CardConfig {
val settingElevation: Dp = 4.dp
val customBackgroundElevation: Dp = 0.dp
// 卡片透明度
var cardAlpha by mutableFloatStateOf(1f)
// 卡片亮度
var cardDim by mutableFloatStateOf(0f)
// 卡片阴影
var cardElevation by mutableStateOf(settingElevation)
var cardElevation by mutableStateOf(0.dp)
var isShadowEnabled by mutableStateOf(true)
var isCustomAlphaSet by mutableStateOf(false)
var isCustomDimSet by mutableStateOf(false)
@@ -69,37 +65,18 @@ object CardConfig {
*/
fun updateShadowEnabled(enabled: Boolean) {
isShadowEnabled = enabled
cardElevation = if (isCustomBackgroundEnabled && cardAlpha != 1f) {
customBackgroundElevation
} else if (enabled) {
settingElevation
} else {
customBackgroundElevation
}
cardElevation = 0.dp
}
/**
* 设置深色模式默认值
* 设置主题模式默认值
*/
fun setDarkModeDefaults() {
if (!isCustomAlphaSet) {
cardAlpha = 0.70f
}
if (!isCustomDimSet) {
cardDim = 0.5f
}
updateShadowEnabled(isShadowEnabled)
}
/**
* 设置浅色模式默认值
*/
fun setLightModeDefaults() {
fun setThemeDefaults(isDarkMode: Boolean) {
if (!isCustomAlphaSet) {
cardAlpha = 1f
}
if (!isCustomDimSet) {
cardDim = 0f
cardDim = if (isDarkMode) 0.5f else 0f
}
updateShadowEnabled(isShadowEnabled)
}
@@ -114,6 +91,19 @@ fun getCardColors(originalColor: Color) = CardDefaults.cardColors(
contentColor = determineContentColor(originalColor)
)
/**
* 获取卡片阴影配置
*/
@Composable
fun getCardElevation() = CardDefaults.cardElevation(
defaultElevation = CardConfig.cardElevation,
pressedElevation = CardConfig.cardElevation,
focusedElevation = CardConfig.cardElevation,
hoveredElevation = CardConfig.cardElevation,
draggedElevation = CardConfig.cardElevation,
disabledElevation = CardConfig.cardElevation
)
/**
* 根据背景颜色、主题模式和用户设置确定内容颜色
*/

View File

@@ -3,260 +3,602 @@ package com.sukisu.ultra.ui.theme
import androidx.compose.ui.graphics.Color
sealed class ThemeColors {
abstract val Primary: Color
abstract val Secondary: Color
abstract val Tertiary: Color
abstract val OnPrimary: Color
abstract val OnSecondary: Color
abstract val OnTertiary: Color
abstract val PrimaryContainer: Color
abstract val SecondaryContainer: Color
abstract val TertiaryContainer: Color
abstract val OnPrimaryContainer: Color
abstract val OnSecondaryContainer: Color
abstract val OnTertiaryContainer: Color
abstract val ButtonContrast: Color
// 表面颜色
abstract val Surface: Color
abstract val SurfaceVariant: Color
abstract val OnSurface: Color
abstract val OnSurfaceVariant: Color
// 错误状态颜色
abstract val Error: Color
abstract val OnError: Color
abstract val ErrorContainer: Color
abstract val OnErrorContainer: Color
// 边框和背景色
abstract val Outline: Color
abstract val OutlineVariant: Color
abstract val Background: Color
abstract val OnBackground: Color
// 浅色
abstract val primaryLight: Color
abstract val onPrimaryLight: Color
abstract val primaryContainerLight: Color
abstract val onPrimaryContainerLight: Color
abstract val secondaryLight: Color
abstract val onSecondaryLight: Color
abstract val secondaryContainerLight: Color
abstract val onSecondaryContainerLight: Color
abstract val tertiaryLight: Color
abstract val onTertiaryLight: Color
abstract val tertiaryContainerLight: Color
abstract val onTertiaryContainerLight: Color
abstract val errorLight: Color
abstract val onErrorLight: Color
abstract val errorContainerLight: Color
abstract val onErrorContainerLight: Color
abstract val backgroundLight: Color
abstract val onBackgroundLight: Color
abstract val surfaceLight: Color
abstract val onSurfaceLight: Color
abstract val surfaceVariantLight: Color
abstract val onSurfaceVariantLight: Color
abstract val outlineLight: Color
abstract val outlineVariantLight: Color
abstract val scrimLight: Color
abstract val inverseSurfaceLight: Color
abstract val inverseOnSurfaceLight: Color
abstract val inversePrimaryLight: Color
abstract val surfaceDimLight: Color
abstract val surfaceBrightLight: Color
abstract val surfaceContainerLowestLight: Color
abstract val surfaceContainerLowLight: Color
abstract val surfaceContainerLight: Color
abstract val surfaceContainerHighLight: Color
abstract val surfaceContainerHighestLight: Color
// 深色
abstract val primaryDark: Color
abstract val onPrimaryDark: Color
abstract val primaryContainerDark: Color
abstract val onPrimaryContainerDark: Color
abstract val secondaryDark: Color
abstract val onSecondaryDark: Color
abstract val secondaryContainerDark: Color
abstract val onSecondaryContainerDark: Color
abstract val tertiaryDark: Color
abstract val onTertiaryDark: Color
abstract val tertiaryContainerDark: Color
abstract val onTertiaryContainerDark: Color
abstract val errorDark: Color
abstract val onErrorDark: Color
abstract val errorContainerDark: Color
abstract val onErrorContainerDark: Color
abstract val backgroundDark: Color
abstract val onBackgroundDark: Color
abstract val surfaceDark: Color
abstract val onSurfaceDark: Color
abstract val surfaceVariantDark: Color
abstract val onSurfaceVariantDark: Color
abstract val outlineDark: Color
abstract val outlineVariantDark: Color
abstract val scrimDark: Color
abstract val inverseSurfaceDark: Color
abstract val inverseOnSurfaceDark: Color
abstract val inversePrimaryDark: Color
abstract val surfaceDimDark: Color
abstract val surfaceBrightDark: Color
abstract val surfaceContainerLowestDark: Color
abstract val surfaceContainerLowDark: Color
abstract val surfaceContainerDark: Color
abstract val surfaceContainerHighDark: Color
abstract val surfaceContainerHighestDark: Color
// 默认主题 (蓝色)
object Default : ThemeColors() {
override val Primary = Color(0xFF2196F3)
override val Secondary = Color(0xFF64B5F6)
override val Tertiary = Color(0xFF0D47A1)
override val OnPrimary = Color(0xFFFFFFFF)
override val OnSecondary = Color(0xFFFFFFFF)
override val OnTertiary = Color(0xFFFFFFFF)
override val PrimaryContainer = Color(0xFFD6EAFF)
override val SecondaryContainer = Color(0xFFE3F2FD)
override val TertiaryContainer = Color(0xFFCFD8DC)
override val OnPrimaryContainer = Color(0xFF0A3049)
override val OnSecondaryContainer = Color(0xFF0D3C61)
override val OnTertiaryContainer = Color(0xFF071D41)
override val ButtonContrast = Color(0xFF2196F3)
override val primaryLight = Color(0xFF415F91)
override val onPrimaryLight = Color(0xFFFFFFFF)
override val primaryContainerLight = Color(0xFFD6E3FF)
override val onPrimaryContainerLight = Color(0xFF284777)
override val secondaryLight = Color(0xFF565F71)
override val onSecondaryLight = Color(0xFFFFFFFF)
override val secondaryContainerLight = Color(0xFFDAE2F9)
override val onSecondaryContainerLight = Color(0xFF3E4759)
override val tertiaryLight = Color(0xFF705575)
override val onTertiaryLight = Color(0xFFFFFFFF)
override val tertiaryContainerLight = Color(0xFFFAD8FD)
override val onTertiaryContainerLight = Color(0xFF573E5C)
override val errorLight = Color(0xFFBA1A1A)
override val onErrorLight = Color(0xFFFFFFFF)
override val errorContainerLight = Color(0xFFFFDAD6)
override val onErrorContainerLight = Color(0xFF93000A)
override val backgroundLight = Color(0xFFF9F9FF)
override val onBackgroundLight = Color(0xFF191C20)
override val surfaceLight = Color(0xFFF9F9FF)
override val onSurfaceLight = Color(0xFF191C20)
override val surfaceVariantLight = Color(0xFFE0E2EC)
override val onSurfaceVariantLight = Color(0xFF44474E)
override val outlineLight = Color(0xFF74777F)
override val outlineVariantLight = Color(0xFFC4C6D0)
override val scrimLight = Color(0xFF000000)
override val inverseSurfaceLight = Color(0xFF2E3036)
override val inverseOnSurfaceLight = Color(0xFFF0F0F7)
override val inversePrimaryLight = Color(0xFFAAC7FF)
override val surfaceDimLight = Color(0xFFD9D9E0)
override val surfaceBrightLight = Color(0xFFF9F9FF)
override val surfaceContainerLowestLight = Color(0xFFFFFFFF)
override val surfaceContainerLowLight = Color(0xFFF3F3FA)
override val surfaceContainerLight = Color(0xFFEDEDF4)
override val surfaceContainerHighLight = Color(0xFFE7E8EE)
override val surfaceContainerHighestLight = Color(0xFFE2E2E9)
override val Surface = Color(0xFFF5F9FF)
override val SurfaceVariant = Color(0xFFEDF5FE)
override val OnSurface = Color(0xFF1A1C1E)
override val OnSurfaceVariant = Color(0xFF42474E)
override val Error = Color(0xFFB00020)
override val OnError = Color(0xFFFFFFFF)
override val ErrorContainer = Color(0xFFFDE7E9)
override val OnErrorContainer = Color(0xFF410008)
override val Outline = Color(0xFFBAC3CF)
override val OutlineVariant = Color(0xFFDFE3EB)
override val Background = Color(0xFFFAFCFF)
override val OnBackground = Color(0xFF1A1C1E)
override val primaryDark = Color(0xFFAAC7FF)
override val onPrimaryDark = Color(0xFF0A305F)
override val primaryContainerDark = Color(0xFF284777)
override val onPrimaryContainerDark = Color(0xFFD6E3FF)
override val secondaryDark = Color(0xFFBEC6DC)
override val onSecondaryDark = Color(0xFF283141)
override val secondaryContainerDark = Color(0xFF3E4759)
override val onSecondaryContainerDark = Color(0xFFDAE2F9)
override val tertiaryDark = Color(0xFFDDBCE0)
override val onTertiaryDark = Color(0xFF3F2844)
override val tertiaryContainerDark = Color(0xFF573E5C)
override val onTertiaryContainerDark = Color(0xFFFAD8FD)
override val errorDark = Color(0xFFFFB4AB)
override val onErrorDark = Color(0xFF690005)
override val errorContainerDark = Color(0xFF93000A)
override val onErrorContainerDark = Color(0xFFFFDAD6)
override val backgroundDark = Color(0xFF111318)
override val onBackgroundDark = Color(0xFFE2E2E9)
override val surfaceDark = Color(0xFF111318)
override val onSurfaceDark = Color(0xFFE2E2E9)
override val surfaceVariantDark = Color(0xFF44474E)
override val onSurfaceVariantDark = Color(0xFFC4C6D0)
override val outlineDark = Color(0xFF8E9099)
override val outlineVariantDark = Color(0xFF44474E)
override val scrimDark = Color(0xFF000000)
override val inverseSurfaceDark = Color(0xFFE2E2E9)
override val inverseOnSurfaceDark = Color(0xFF2E3036)
override val inversePrimaryDark = Color(0xFF415F91)
override val surfaceDimDark = Color(0xFF111318)
override val surfaceBrightDark = Color(0xFF37393E)
override val surfaceContainerLowestDark = Color(0xFF0C0E13)
override val surfaceContainerLowDark = Color(0xFF191C20)
override val surfaceContainerDark = Color(0xFF1D2024)
override val surfaceContainerHighDark = Color(0xFF282A2F)
override val surfaceContainerHighestDark = Color(0xFF33353A)
}
// 绿色主题
object Green : ThemeColors() {
override val Primary = Color(0xFF43A047)
override val Secondary = Color(0xFF66BB6A)
override val Tertiary = Color(0xFF1B5E20)
override val OnPrimary = Color(0xFFFFFFFF)
override val OnSecondary = Color(0xFFFFFFFF)
override val OnTertiary = Color(0xFFFFFFFF)
override val PrimaryContainer = Color(0xFFD8EFDB)
override val SecondaryContainer = Color(0xFFE8F5E9)
override val TertiaryContainer = Color(0xFFB9F6CA)
override val OnPrimaryContainer = Color(0xFF0A280D)
override val OnSecondaryContainer = Color(0xFF0E2912)
override val OnTertiaryContainer = Color(0xFF051B07)
override val ButtonContrast = Color(0xFF43A047)
override val primaryLight = Color(0xFF4C662B)
override val onPrimaryLight = Color(0xFFFFFFFF)
override val primaryContainerLight = Color(0xFFCDEDA3)
override val onPrimaryContainerLight = Color(0xFF354E16)
override val secondaryLight = Color(0xFF586249)
override val onSecondaryLight = Color(0xFFFFFFFF)
override val secondaryContainerLight = Color(0xFFDCE7C8)
override val onSecondaryContainerLight = Color(0xFF404A33)
override val tertiaryLight = Color(0xFF386663)
override val onTertiaryLight = Color(0xFFFFFFFF)
override val tertiaryContainerLight = Color(0xFFBCECE7)
override val onTertiaryContainerLight = Color(0xFF1F4E4B)
override val errorLight = Color(0xFFBA1A1A)
override val onErrorLight = Color(0xFFFFFFFF)
override val errorContainerLight = Color(0xFFFFDAD6)
override val onErrorContainerLight = Color(0xFF93000A)
override val backgroundLight = Color(0xFFF9FAEF)
override val onBackgroundLight = Color(0xFF1A1C16)
override val surfaceLight = Color(0xFFF9FAEF)
override val onSurfaceLight = Color(0xFF1A1C16)
override val surfaceVariantLight = Color(0xFFE1E4D5)
override val onSurfaceVariantLight = Color(0xFF44483D)
override val outlineLight = Color(0xFF75796C)
override val outlineVariantLight = Color(0xFFC5C8BA)
override val scrimLight = Color(0xFF000000)
override val inverseSurfaceLight = Color(0xFF2F312A)
override val inverseOnSurfaceLight = Color(0xFFF1F2E6)
override val inversePrimaryLight = Color(0xFFB1D18A)
override val surfaceDimLight = Color(0xFFDADBD0)
override val surfaceBrightLight = Color(0xFFF9FAEF)
override val surfaceContainerLowestLight = Color(0xFFFFFFFF)
override val surfaceContainerLowLight = Color(0xFFF3F4E9)
override val surfaceContainerLight = Color(0xFFEEEFE3)
override val surfaceContainerHighLight = Color(0xFFE8E9DE)
override val surfaceContainerHighestLight = Color(0xFFE2E3D8)
override val Surface = Color(0xFFF6FBF6)
override val SurfaceVariant = Color(0xFFEDF7EE)
override val OnSurface = Color(0xFF191C19)
override val OnSurfaceVariant = Color(0xFF414941)
override val Error = Color(0xFFC62828)
override val OnError = Color(0xFFFFFFFF)
override val ErrorContainer = Color(0xFFF8D7DA)
override val OnErrorContainer = Color(0xFF4A0808)
override val Outline = Color(0xFFBDC9BF)
override val OutlineVariant = Color(0xFFDDE6DE)
override val Background = Color(0xFFFBFDFB)
override val OnBackground = Color(0xFF191C19)
override val primaryDark = Color(0xFFB1D18A)
override val onPrimaryDark = Color(0xFF1F3701)
override val primaryContainerDark = Color(0xFF354E16)
override val onPrimaryContainerDark = Color(0xFFCDEDA3)
override val secondaryDark = Color(0xFFBFCBAD)
override val onSecondaryDark = Color(0xFF2A331E)
override val secondaryContainerDark = Color(0xFF404A33)
override val onSecondaryContainerDark = Color(0xFFDCE7C8)
override val tertiaryDark = Color(0xFFA0D0CB)
override val onTertiaryDark = Color(0xFF003735)
override val tertiaryContainerDark = Color(0xFF1F4E4B)
override val onTertiaryContainerDark = Color(0xFFBCECE7)
override val errorDark = Color(0xFFFFB4AB)
override val onErrorDark = Color(0xFF690005)
override val errorContainerDark = Color(0xFF93000A)
override val onErrorContainerDark = Color(0xFFFFDAD6)
override val backgroundDark = Color(0xFF12140E)
override val onBackgroundDark = Color(0xFFE2E3D8)
override val surfaceDark = Color(0xFF12140E)
override val onSurfaceDark = Color(0xFFE2E3D8)
override val surfaceVariantDark = Color(0xFF44483D)
override val onSurfaceVariantDark = Color(0xFFC5C8BA)
override val outlineDark = Color(0xFF8F9285)
override val outlineVariantDark = Color(0xFF44483D)
override val scrimDark = Color(0xFF000000)
override val inverseSurfaceDark = Color(0xFFE2E3D8)
override val inverseOnSurfaceDark = Color(0xFF2F312A)
override val inversePrimaryDark = Color(0xFF4C662B)
override val surfaceDimDark = Color(0xFF12140E)
override val surfaceBrightDark = Color(0xFF383A32)
override val surfaceContainerLowestDark = Color(0xFF0C0F09)
override val surfaceContainerLowDark = Color(0xFF1A1C16)
override val surfaceContainerDark = Color(0xFF1E201A)
override val surfaceContainerHighDark = Color(0xFF282B24)
override val surfaceContainerHighestDark = Color(0xFF33362E)
}
// 紫色主题
object Purple : ThemeColors() {
override val Primary = Color(0xFF9C27B0)
override val Secondary = Color(0xFFBA68C8)
override val Tertiary = Color(0xFF6A1B9A)
override val OnPrimary = Color(0xFFFFFFFF)
override val OnSecondary = Color(0xFFFFFFFF)
override val OnTertiary = Color(0xFFFFFFFF)
override val PrimaryContainer = Color(0xFFF3D8F8)
override val SecondaryContainer = Color(0xFFF5E9F7)
override val TertiaryContainer = Color(0xFFE1BEE7)
override val OnPrimaryContainer = Color(0xFF2A0934)
override val OnSecondaryContainer = Color(0xFF3C0F50)
override val OnTertiaryContainer = Color(0xFF1D0830)
override val ButtonContrast = Color(0xFF9C27B0)
override val primaryLight = Color(0xFF7C4E7E)
override val onPrimaryLight = Color(0xFFFFFFFF)
override val primaryContainerLight = Color(0xFFFFD6FC)
override val onPrimaryContainerLight = Color(0xFF623765)
override val secondaryLight = Color(0xFF6C586B)
override val onSecondaryLight = Color(0xFFFFFFFF)
override val secondaryContainerLight = Color(0xFFF5DBF1)
override val onSecondaryContainerLight = Color(0xFF534152)
override val tertiaryLight = Color(0xFF825249)
override val onTertiaryLight = Color(0xFFFFFFFF)
override val tertiaryContainerLight = Color(0xFFFFDAD4)
override val onTertiaryContainerLight = Color(0xFF673B33)
override val errorLight = Color(0xFFBA1A1A)
override val onErrorLight = Color(0xFFFFFFFF)
override val errorContainerLight = Color(0xFFFFDAD6)
override val onErrorContainerLight = Color(0xFF93000A)
override val backgroundLight = Color(0xFFFFF7FA)
override val onBackgroundLight = Color(0xFF1F1A1F)
override val surfaceLight = Color(0xFFFFF7FA)
override val onSurfaceLight = Color(0xFF1F1A1F)
override val surfaceVariantLight = Color(0xFFEDDFE8)
override val onSurfaceVariantLight = Color(0xFF4D444C)
override val outlineLight = Color(0xFF7F747C)
override val outlineVariantLight = Color(0xFFD0C3CC)
override val scrimLight = Color(0xFF000000)
override val inverseSurfaceLight = Color(0xFF352F34)
override val inverseOnSurfaceLight = Color(0xFFF9EEF4)
override val inversePrimaryLight = Color(0xFFECB4EC)
override val surfaceDimLight = Color(0xFFE2D7DE)
override val surfaceBrightLight = Color(0xFFFFF7FA)
override val surfaceContainerLowestLight = Color(0xFFFFFFFF)
override val surfaceContainerLowLight = Color(0xFFFCF0F7)
override val surfaceContainerLight = Color(0xFFF6EBF2)
override val surfaceContainerHighLight = Color(0xFFF0E5EC)
override val surfaceContainerHighestLight = Color(0xFFEBDFE6)
override val Surface = Color(0xFFFCF6FF)
override val SurfaceVariant = Color(0xFFF5EEFA)
override val OnSurface = Color(0xFF1D1B1E)
override val OnSurfaceVariant = Color(0xFF49454E)
override val Error = Color(0xFFD50000)
override val OnError = Color(0xFFFFFFFF)
override val ErrorContainer = Color(0xFFFFDCD5)
override val OnErrorContainer = Color(0xFF480000)
override val Outline = Color(0xFFC9B9D0)
override val OutlineVariant = Color(0xFFE8DAED)
override val Background = Color(0xFFFFFBFF)
override val OnBackground = Color(0xFF1D1B1E)
override val primaryDark = Color(0xFFECB4EC)
override val onPrimaryDark = Color(0xFF49204D)
override val primaryContainerDark = Color(0xFF623765)
override val onPrimaryContainerDark = Color(0xFFFFD6FC)
override val secondaryDark = Color(0xFFD8BFD5)
override val onSecondaryDark = Color(0xFF3B2B3B)
override val secondaryContainerDark = Color(0xFF534152)
override val onSecondaryContainerDark = Color(0xFFF5DBF1)
override val tertiaryDark = Color(0xFFF6B8AD)
override val onTertiaryDark = Color(0xFF4C251F)
override val tertiaryContainerDark = Color(0xFF673B33)
override val onTertiaryContainerDark = Color(0xFFFFDAD4)
override val errorDark = Color(0xFFFFB4AB)
override val onErrorDark = Color(0xFF690005)
override val errorContainerDark = Color(0xFF93000A)
override val onErrorContainerDark = Color(0xFFFFDAD6)
override val backgroundDark = Color(0xFF171216)
override val onBackgroundDark = Color(0xFFEBDFE6)
override val surfaceDark = Color(0xFF171216)
override val onSurfaceDark = Color(0xFFEBDFE6)
override val surfaceVariantDark = Color(0xFF4D444C)
override val onSurfaceVariantDark = Color(0xFFD0C3CC)
override val outlineDark = Color(0xFF998D96)
override val outlineVariantDark = Color(0xFF4D444C)
override val scrimDark = Color(0xFF000000)
override val inverseSurfaceDark = Color(0xFFEBDFE6)
override val inverseOnSurfaceDark = Color(0xFF352F34)
override val inversePrimaryDark = Color(0xFF7C4E7E)
override val surfaceDimDark = Color(0xFF171216)
override val surfaceBrightDark = Color(0xFF3E373D)
override val surfaceContainerLowestDark = Color(0xFF110D11)
override val surfaceContainerLowDark = Color(0xFF1F1A1F)
override val surfaceContainerDark = Color(0xFF231E23)
override val surfaceContainerHighDark = Color(0xFF2E282D)
override val surfaceContainerHighestDark = Color(0xFF393338)
}
// 橙色主题
object Orange : ThemeColors() {
override val Primary = Color(0xFFFF9800)
override val Secondary = Color(0xFFFFB74D)
override val Tertiary = Color(0xFFE65100)
override val OnPrimary = Color(0xFFFFFFFF)
override val OnSecondary = Color(0xFF000000)
override val OnTertiary = Color(0xFFFFFFFF)
override val PrimaryContainer = Color(0xFFFFECCC)
override val SecondaryContainer = Color(0xFFFFF0D9)
override val TertiaryContainer = Color(0xFFFFD180)
override val OnPrimaryContainer = Color(0xFF351F00)
override val OnSecondaryContainer = Color(0xFF3D2800)
override val OnTertiaryContainer = Color(0xFF2E1500)
override val ButtonContrast = Color(0xFFFF9800)
override val primaryLight = Color(0xFF8B4F24)
override val onPrimaryLight = Color(0xFFFFFFFF)
override val primaryContainerLight = Color(0xFFFFDCC7)
override val onPrimaryContainerLight = Color(0xFF6E390E)
override val secondaryLight = Color(0xFF755846)
override val onSecondaryLight = Color(0xFFFFFFFF)
override val secondaryContainerLight = Color(0xFFFFDCC7)
override val onSecondaryContainerLight = Color(0xFF5B4130)
override val tertiaryLight = Color(0xFF865219)
override val onTertiaryLight = Color(0xFFFFFFFF)
override val tertiaryContainerLight = Color(0xFFFFDCBF)
override val onTertiaryContainerLight = Color(0xFF6A3B01)
override val errorLight = Color(0xFFBA1A1A)
override val onErrorLight = Color(0xFFFFFFFF)
override val errorContainerLight = Color(0xFFFFDAD6)
override val onErrorContainerLight = Color(0xFF93000A)
override val backgroundLight = Color(0xFFFFF8F5)
override val onBackgroundLight = Color(0xFF221A15)
override val surfaceLight = Color(0xFFFFF8F5)
override val onSurfaceLight = Color(0xFF221A15)
override val surfaceVariantLight = Color(0xFFF4DED3)
override val onSurfaceVariantLight = Color(0xFF52443C)
override val outlineLight = Color(0xFF84746A)
override val outlineVariantLight = Color(0xFFD7C3B8)
override val scrimLight = Color(0xFF000000)
override val inverseSurfaceLight = Color(0xFF382E29)
override val inverseOnSurfaceLight = Color(0xFFFFEDE5)
override val inversePrimaryLight = Color(0xFFFFB787)
override val surfaceDimLight = Color(0xFFE7D7CE)
override val surfaceBrightLight = Color(0xFFFFF8F5)
override val surfaceContainerLowestLight = Color(0xFFFFFFFF)
override val surfaceContainerLowLight = Color(0xFFFFF1EA)
override val surfaceContainerLight = Color(0xFFFCEBE2)
override val surfaceContainerHighLight = Color(0xFFF6E5DC)
override val surfaceContainerHighestLight = Color(0xFFF0DFD7)
override val Surface = Color(0xFFFFF8F3)
override val SurfaceVariant = Color(0xFFFFF0E6)
override val OnSurface = Color(0xFF1F1B16)
override val OnSurfaceVariant = Color(0xFF4E4639)
override val Error = Color(0xFFD32F2F)
override val OnError = Color(0xFFFFFFFF)
override val ErrorContainer = Color(0xFFFFDBC8)
override val OnErrorContainer = Color(0xFF490700)
override val Outline = Color(0xFFD6C3AD)
override val OutlineVariant = Color(0xFFEFDFCC)
override val Background = Color(0xFFFFFBFF)
override val OnBackground = Color(0xFF1F1B16)
override val primaryDark = Color(0xFFFFB787)
override val onPrimaryDark = Color(0xFF502400)
override val primaryContainerDark = Color(0xFF6E390E)
override val onPrimaryContainerDark = Color(0xFFFFDCC7)
override val secondaryDark = Color(0xFFE5BFA8)
override val onSecondaryDark = Color(0xFF422B1B)
override val secondaryContainerDark = Color(0xFF5B4130)
override val onSecondaryContainerDark = Color(0xFFFFDCC7)
override val tertiaryDark = Color(0xFFFDB876)
override val onTertiaryDark = Color(0xFF4B2800)
override val tertiaryContainerDark = Color(0xFF6A3B01)
override val onTertiaryContainerDark = Color(0xFFFFDCBF)
override val errorDark = Color(0xFFFFB4AB)
override val onErrorDark = Color(0xFF690005)
override val errorContainerDark = Color(0xFF93000A)
override val onErrorContainerDark = Color(0xFFFFDAD6)
override val backgroundDark = Color(0xFF19120D)
override val onBackgroundDark = Color(0xFFF0DFD7)
override val surfaceDark = Color(0xFF19120D)
override val onSurfaceDark = Color(0xFFF0DFD7)
override val surfaceVariantDark = Color(0xFF52443C)
override val onSurfaceVariantDark = Color(0xFFD7C3B8)
override val outlineDark = Color(0xFF9F8D83)
override val outlineVariantDark = Color(0xFF52443C)
override val scrimDark = Color(0xFF000000)
override val inverseSurfaceDark = Color(0xFFF0DFD7)
override val inverseOnSurfaceDark = Color(0xFF382E29)
override val inversePrimaryDark = Color(0xFF8B4F24)
override val surfaceDimDark = Color(0xFF19120D)
override val surfaceBrightDark = Color(0xFF413731)
override val surfaceContainerLowestDark = Color(0xFF140D08)
override val surfaceContainerLowDark = Color(0xFF221A15)
override val surfaceContainerDark = Color(0xFF261E19)
override val surfaceContainerHighDark = Color(0xFF312823)
override val surfaceContainerHighestDark = Color(0xFF3D332D)
}
// 粉色主题
object Pink : ThemeColors() {
override val Primary = Color(0xFFE91E63)
override val Secondary = Color(0xFFF06292)
override val Tertiary = Color(0xFF880E4F)
override val OnPrimary = Color(0xFFFFFFFF)
override val OnSecondary = Color(0xFFFFFFFF)
override val OnTertiary = Color(0xFFFFFFFF)
override val PrimaryContainer = Color(0xFFFCE4EC)
override val SecondaryContainer = Color(0xFFFCE4EC)
override val TertiaryContainer = Color(0xFFF8BBD0)
override val OnPrimaryContainer = Color(0xFF3B0819)
override val OnSecondaryContainer = Color(0xFF3B0819)
override val OnTertiaryContainer = Color(0xFF2B0516)
override val ButtonContrast = Color(0xFFE91E63)
override val primaryLight = Color(0xFF8C4A60)
override val onPrimaryLight = Color(0xFFFFFFFF)
override val primaryContainerLight = Color(0xFFFFD9E2)
override val onPrimaryContainerLight = Color(0xFF703348)
override val secondaryLight = Color(0xFF8B4A62)
override val onSecondaryLight = Color(0xFFFFFFFF)
override val secondaryContainerLight = Color(0xFFFFD9E3)
override val onSecondaryContainerLight = Color(0xFF6F334B)
override val tertiaryLight = Color(0xFF8B4A62)
override val onTertiaryLight = Color(0xFFFFFFFF)
override val tertiaryContainerLight = Color(0xFFFFD9E3)
override val onTertiaryContainerLight = Color(0xFF6F334B)
override val errorLight = Color(0xFFBA1A1A)
override val onErrorLight = Color(0xFFFFFFFF)
override val errorContainerLight = Color(0xFFFFDAD6)
override val onErrorContainerLight = Color(0xFF93000A)
override val backgroundLight = Color(0xFFFFF8F8)
override val onBackgroundLight = Color(0xFF22191B)
override val surfaceLight = Color(0xFFFFF8F8)
override val onSurfaceLight = Color(0xFF22191B)
override val surfaceVariantLight = Color(0xFFF2DDE1)
override val onSurfaceVariantLight = Color(0xFF514346)
override val outlineLight = Color(0xFF837377)
override val outlineVariantLight = Color(0xFFD5C2C5)
override val scrimLight = Color(0xFF000000)
override val inverseSurfaceLight = Color(0xFF372E30)
override val inverseOnSurfaceLight = Color(0xFFFDEDEF)
override val inversePrimaryLight = Color(0xFFFFB1C7)
override val surfaceDimLight = Color(0xFFE6D6D9)
override val surfaceBrightLight = Color(0xFFFFF8F8)
override val surfaceContainerLowestLight = Color(0xFFFFFFFF)
override val surfaceContainerLowLight = Color(0xFFFFF0F2)
override val surfaceContainerLight = Color(0xFFFBEAED)
override val surfaceContainerHighLight = Color(0xFFF5E4E7)
override val surfaceContainerHighestLight = Color(0xFFEFDFE1)
override val Surface = Color(0xFFFFF7F9)
override val SurfaceVariant = Color(0xFFFCEEF2)
override val OnSurface = Color(0xFF201A1C)
override val OnSurfaceVariant = Color(0xFF534347)
override val Error = Color(0xFFB71C1C)
override val OnError = Color(0xFFFFFFFF)
override val ErrorContainer = Color(0xFFFFDAD6)
override val OnErrorContainer = Color(0xFF410002)
override val Outline = Color(0xFFD6BABF)
override val OutlineVariant = Color(0xFFEFDDE0)
override val Background = Color(0xFFFFFBFF)
override val OnBackground = Color(0xFF201A1C)
override val primaryDark = Color(0xFFFFB1C7)
override val onPrimaryDark = Color(0xFF541D32)
override val primaryContainerDark = Color(0xFF703348)
override val onPrimaryContainerDark = Color(0xFFFFD9E2)
override val secondaryDark = Color(0xFFFFB0CB)
override val onSecondaryDark = Color(0xFF541D34)
override val secondaryContainerDark = Color(0xFF6F334B)
override val onSecondaryContainerDark = Color(0xFFFFD9E3)
override val tertiaryDark = Color(0xFFFFB0CB)
override val onTertiaryDark = Color(0xFF541D34)
override val tertiaryContainerDark = Color(0xFF6F334B)
override val onTertiaryContainerDark = Color(0xFFFFD9E3)
override val errorDark = Color(0xFFFFB4AB)
override val onErrorDark = Color(0xFF690005)
override val errorContainerDark = Color(0xFF93000A)
override val onErrorContainerDark = Color(0xFFFFDAD6)
override val backgroundDark = Color(0xFF191113)
override val onBackgroundDark = Color(0xFFEFDFE1)
override val surfaceDark = Color(0xFF191113)
override val onSurfaceDark = Color(0xFFEFDFE1)
override val surfaceVariantDark = Color(0xFF514346)
override val onSurfaceVariantDark = Color(0xFFD5C2C5)
override val outlineDark = Color(0xFF9E8C90)
override val outlineVariantDark = Color(0xFF514346)
override val scrimDark = Color(0xFF000000)
override val inverseSurfaceDark = Color(0xFFEFDFE1)
override val inverseOnSurfaceDark = Color(0xFF372E30)
override val inversePrimaryDark = Color(0xFF8C4A60)
override val surfaceDimDark = Color(0xFF191113)
override val surfaceBrightDark = Color(0xFF413739)
override val surfaceContainerLowestDark = Color(0xFF140C0E)
override val surfaceContainerLowDark = Color(0xFF22191B)
override val surfaceContainerDark = Color(0xFF261D1F)
override val surfaceContainerHighDark = Color(0xFF31282A)
override val surfaceContainerHighestDark = Color(0xFF3C3234)
}
// 灰色主题
object Gray : ThemeColors() {
override val Primary = Color(0xFF607D8B)
override val Secondary = Color(0xFF90A4AE)
override val Tertiary = Color(0xFF455A64)
override val OnPrimary = Color(0xFFFFFFFF)
override val OnSecondary = Color(0xFFFFFFFF)
override val OnTertiary = Color(0xFFFFFFFF)
override val PrimaryContainer = Color(0xFFECEFF1)
override val SecondaryContainer = Color(0xFFECEFF1)
override val TertiaryContainer = Color(0xFFCFD8DC)
override val OnPrimaryContainer = Color(0xFF1A2327)
override val OnSecondaryContainer = Color(0xFF1A2327)
override val OnTertiaryContainer = Color(0xFF121A1D)
override val ButtonContrast = Color(0xFF607D8B)
override val primaryLight = Color(0xFF5B5C5C)
override val onPrimaryLight = Color(0xFFFFFFFF)
override val primaryContainerLight = Color(0xFF747474)
override val onPrimaryContainerLight = Color(0xFFFEFCFC)
override val secondaryLight = Color(0xFF5F5E5E)
override val onSecondaryLight = Color(0xFFFFFFFF)
override val secondaryContainerLight = Color(0xFFE4E2E1)
override val onSecondaryContainerLight = Color(0xFF656464)
override val tertiaryLight = Color(0xFF5E5B5D)
override val onTertiaryLight = Color(0xFFFFFFFF)
override val tertiaryContainerLight = Color(0xFF777375)
override val onTertiaryContainerLight = Color(0xFFFFFBFF)
override val errorLight = Color(0xFFBA1A1A)
override val onErrorLight = Color(0xFFFFFFFF)
override val errorContainerLight = Color(0xFFFFDAD6)
override val onErrorContainerLight = Color(0xFF93000A)
override val backgroundLight = Color(0xFFFCF8F8)
override val onBackgroundLight = Color(0xFF1C1B1B)
override val surfaceLight = Color(0xFFFCF8F8)
override val onSurfaceLight = Color(0xFF1C1B1B)
override val surfaceVariantLight = Color(0xFFE0E3E3)
override val onSurfaceVariantLight = Color(0xFF444748)
override val outlineLight = Color(0xFF747878)
override val outlineVariantLight = Color(0xFFC4C7C7)
override val scrimLight = Color(0xFF000000)
override val inverseSurfaceLight = Color(0xFF313030)
override val inverseOnSurfaceLight = Color(0xFFF4F0EF)
override val inversePrimaryLight = Color(0xFFC7C6C6)
override val surfaceDimLight = Color(0xFFDDD9D8)
override val surfaceBrightLight = Color(0xFFFCF8F8)
override val surfaceContainerLowestLight = Color(0xFFFFFFFF)
override val surfaceContainerLowLight = Color(0xFFF7F3F2)
override val surfaceContainerLight = Color(0xFFF1EDEC)
override val surfaceContainerHighLight = Color(0xFFEBE7E7)
override val surfaceContainerHighestLight = Color(0xFFE5E2E1)
override val Surface = Color(0xFFF6F9FB)
override val SurfaceVariant = Color(0xFFEEF2F4)
override val OnSurface = Color(0xFF191C1E)
override val OnSurfaceVariant = Color(0xFF41484D)
override val Error = Color(0xFFC62828)
override val OnError = Color(0xFFFFFFFF)
override val ErrorContainer = Color(0xFFFFDAD6)
override val OnErrorContainer = Color(0xFF410002)
override val Outline = Color(0xFFBDC1C4)
override val OutlineVariant = Color(0xFFDDE1E3)
override val Background = Color(0xFFFBFCFE)
override val OnBackground = Color(0xFF191C1E)
override val primaryDark = Color(0xFFC7C6C6)
override val onPrimaryDark = Color(0xFF303031)
override val primaryContainerDark = Color(0xFF919190)
override val onPrimaryContainerDark = Color(0xFF161718)
override val secondaryDark = Color(0xFFC8C6C5)
override val onSecondaryDark = Color(0xFF303030)
override val secondaryContainerDark = Color(0xFF474746)
override val onSecondaryContainerDark = Color(0xFFB7B5B4)
override val tertiaryDark = Color(0xFFCAC5C7)
override val onTertiaryDark = Color(0xFF323031)
override val tertiaryContainerDark = Color(0xFF948F91)
override val onTertiaryContainerDark = Color(0xFF181718)
override val errorDark = Color(0xFFFFB4AB)
override val onErrorDark = Color(0xFF690005)
override val errorContainerDark = Color(0xFF93000A)
override val onErrorContainerDark = Color(0xFFFFDAD6)
override val backgroundDark = Color(0xFF141313)
override val onBackgroundDark = Color(0xFFE5E2E1)
override val surfaceDark = Color(0xFF141313)
override val onSurfaceDark = Color(0xFFE5E2E1)
override val surfaceVariantDark = Color(0xFF444748)
override val onSurfaceVariantDark = Color(0xFFC4C7C7)
override val outlineDark = Color(0xFF8E9192)
override val outlineVariantDark = Color(0xFF444748)
override val scrimDark = Color(0xFF000000)
override val inverseSurfaceDark = Color(0xFFE5E2E1)
override val inverseOnSurfaceDark = Color(0xFF313030)
override val inversePrimaryDark = Color(0xFF5E5E5E)
override val surfaceDimDark = Color(0xFF141313)
override val surfaceBrightDark = Color(0xFF3A3939)
override val surfaceContainerLowestDark = Color(0xFF0E0E0E)
override val surfaceContainerLowDark = Color(0xFF1C1B1B)
override val surfaceContainerDark = Color(0xFF201F1F)
override val surfaceContainerHighDark = Color(0xFF2A2A2A)
override val surfaceContainerHighestDark = Color(0xFF353434)
}
// 黄色主题
object Yellow : ThemeColors() {
override val Primary = Color(0xFFFFC107)
override val Secondary = Color(0xFFFFD54F)
override val Tertiary = Color(0xFFFF8F00)
override val OnPrimary = Color(0xFF000000)
override val OnSecondary = Color(0xFF000000)
override val OnTertiary = Color(0xFFFFFFFF)
override val PrimaryContainer = Color(0xFFFFF8E1)
override val SecondaryContainer = Color(0xFFFFF8E1)
override val TertiaryContainer = Color(0xFFFFECB3)
override val OnPrimaryContainer = Color(0xFF332A00)
override val OnSecondaryContainer = Color(0xFF332A00)
override val OnTertiaryContainer = Color(0xFF221200)
override val ButtonContrast = Color(0xFFFFC107)
override val primaryLight = Color(0xFF6D5E0F)
override val onPrimaryLight = Color(0xFFFFFFFF)
override val primaryContainerLight = Color(0xFFF8E288)
override val onPrimaryContainerLight = Color(0xFF534600)
override val secondaryLight = Color(0xFF6D5E0F)
override val onSecondaryLight = Color(0xFFFFFFFF)
override val secondaryContainerLight = Color(0xFFF7E388)
override val onSecondaryContainerLight = Color(0xFF534600)
override val tertiaryLight = Color(0xFF685F13)
override val onTertiaryLight = Color(0xFFFFFFFF)
override val tertiaryContainerLight = Color(0xFFF1E58A)
override val onTertiaryContainerLight = Color(0xFF4F4800)
override val errorLight = Color(0xFFBA1A1A)
override val onErrorLight = Color(0xFFFFFFFF)
override val errorContainerLight = Color(0xFFFFDAD6)
override val onErrorContainerLight = Color(0xFF93000A)
override val backgroundLight = Color(0xFFFFF9ED)
override val onBackgroundLight = Color(0xFF1E1C13)
override val surfaceLight = Color(0xFFFFF9ED)
override val onSurfaceLight = Color(0xFF1E1C13)
override val surfaceVariantLight = Color(0xFFE9E2D0)
override val onSurfaceVariantLight = Color(0xFF4B4739)
override val outlineLight = Color(0xFF7C7768)
override val outlineVariantLight = Color(0xFFCDC6B4)
override val scrimLight = Color(0xFF000000)
override val inverseSurfaceLight = Color(0xFF333027)
override val inverseOnSurfaceLight = Color(0xFFF7F0E2)
override val inversePrimaryLight = Color(0xFFDAC66F)
override val surfaceDimLight = Color(0xFFE0D9CC)
override val surfaceBrightLight = Color(0xFFFFF9ED)
override val surfaceContainerLowestLight = Color(0xFFFFFFFF)
override val surfaceContainerLowLight = Color(0xFFFAF3E5)
override val surfaceContainerLight = Color(0xFFF4EDDF)
override val surfaceContainerHighLight = Color(0xFFEEE8DA)
override val surfaceContainerHighestLight = Color(0xFFE8E2D4)
override val Surface = Color(0xFFFFFAF3)
override val SurfaceVariant = Color(0xFFFFF7E6)
override val OnSurface = Color(0xFF1F1C17)
override val OnSurfaceVariant = Color(0xFF4E4A3C)
override val Error = Color(0xFFB71C1C)
override val OnError = Color(0xFFFFFFFF)
override val ErrorContainer = Color(0xFFFFDAD6)
override val OnErrorContainer = Color(0xFF410002)
override val Outline = Color(0xFFD1C8AF)
override val OutlineVariant = Color(0xFFEEE8D7)
override val Background = Color(0xFFFFFCF8)
override val OnBackground = Color(0xFF1F1C17)
override val primaryDark = Color(0xFFDAC66F)
override val onPrimaryDark = Color(0xFF393000)
override val primaryContainerDark = Color(0xFF534600)
override val onPrimaryContainerDark = Color(0xFFF8E288)
override val secondaryDark = Color(0xFFDAC76F)
override val onSecondaryDark = Color(0xFF393000)
override val secondaryContainerDark = Color(0xFF534600)
override val onSecondaryContainerDark = Color(0xFFF7E388)
override val tertiaryDark = Color(0xFFD4C871)
override val onTertiaryDark = Color(0xFF363100)
override val tertiaryContainerDark = Color(0xFF4F4800)
override val onTertiaryContainerDark = Color(0xFFF1E58A)
override val errorDark = Color(0xFFFFB4AB)
override val onErrorDark = Color(0xFF690005)
override val errorContainerDark = Color(0xFF93000A)
override val onErrorContainerDark = Color(0xFFFFDAD6)
override val backgroundDark = Color(0xFF15130B)
override val onBackgroundDark = Color(0xFFE8E2D4)
override val surfaceDark = Color(0xFF15130B)
override val onSurfaceDark = Color(0xFFE8E2D4)
override val surfaceVariantDark = Color(0xFF4B4739)
override val onSurfaceVariantDark = Color(0xFFCDC6B4)
override val outlineDark = Color(0xFF969080)
override val outlineVariantDark = Color(0xFF4B4739)
override val scrimDark = Color(0xFF000000)
override val inverseSurfaceDark = Color(0xFFE8E2D4)
override val inverseOnSurfaceDark = Color(0xFF333027)
override val inversePrimaryDark = Color(0xFF6D5E0F)
override val surfaceDimDark = Color(0xFF15130B)
override val surfaceBrightDark = Color(0xFF3C3930)
override val surfaceContainerLowestDark = Color(0xFF100E07)
override val surfaceContainerLowDark = Color(0xFF1E1C13)
override val surfaceContainerDark = Color(0xFF222017)
override val surfaceContainerHighDark = Color(0xFF2C2A21)
override val surfaceContainerHighestDark = Color(0xFF37352B)
}
companion object {

View File

@@ -47,6 +47,7 @@ import com.sukisu.ultra.ui.util.saveTransformedBackground
import androidx.activity.SystemBarStyle
import androidx.activity.ComponentActivity
import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
@@ -150,9 +151,9 @@ fun KernelSUTheme(
// 根据暗色模式和自定义背景调整卡片配置
val isDarkModeWithCustomBackground = darkTheme && ThemeConfig.customBackgroundUri != null
if (darkTheme && !dynamicColor) {
CardConfig.setDarkModeDefaults()
CardConfig.setThemeDefaults(true)
} else if (!darkTheme && !dynamicColor) {
CardConfig.setLightModeDefaults()
CardConfig.setThemeDefaults(false)
}
CardConfig.updateShadowEnabled(!isDarkModeWithCustomBackground)
@@ -216,7 +217,8 @@ fun KernelSUTheme(
modifier = Modifier
.fillMaxSize()
.zIndex(-2f)
.background(if (darkTheme) Color.Black else Color.White)
.background(if (darkTheme) if (CardConfig.isCustomBackgroundEnabled) { colorScheme.surfaceContainerLow } else { colorScheme.background }
else if (CardConfig.isCustomBackgroundEnabled) { colorScheme.surfaceContainerLow } else { colorScheme.background })
)
// 自定义背景层
@@ -287,54 +289,73 @@ fun KernelSUTheme(
*/
@RequiresApi(Build.VERSION_CODES.S)
@Composable
private fun createDynamicDarkColorScheme(context: Context) =
dynamicDarkColorScheme(context).copy(
background = Color.Transparent,
surface = Color.Transparent,
onBackground = Color.White,
onSurface = Color.White
private fun createDynamicDarkColorScheme(context: Context): ColorScheme {
val scheme = dynamicDarkColorScheme(context)
return scheme.copy(
background = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else scheme.background,
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else scheme.surface,
onBackground = scheme.onBackground,
onSurface = scheme.onSurface
)
}
/**
* 创建动态浅色颜色方案
*/
@RequiresApi(Build.VERSION_CODES.S)
@Composable
private fun createDynamicLightColorScheme(context: Context) =
dynamicLightColorScheme(context).copy(
background = Color.Transparent,
surface = Color.Transparent
private fun createDynamicLightColorScheme(context: Context): ColorScheme {
val scheme = dynamicLightColorScheme(context)
return scheme.copy(
background = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else scheme.background,
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else scheme.surface,
onBackground = scheme.onBackground,
onSurface = scheme.onSurface
)
}
/**
* 创建深色颜色方案
*/
@Composable
private fun createDarkColorScheme() = darkColorScheme(
primary = ThemeConfig.currentTheme.Primary.copy(alpha = 0.8f),
onPrimary = Color.White,
primaryContainer = ThemeConfig.currentTheme.PrimaryContainer.copy(alpha = 0.15f),
onPrimaryContainer = Color.White,
secondary = ThemeConfig.currentTheme.Secondary.copy(alpha = 0.8f),
onSecondary = Color.White,
secondaryContainer = ThemeConfig.currentTheme.SecondaryContainer.copy(alpha = 0.15f),
onSecondaryContainer = Color.White,
tertiary = ThemeConfig.currentTheme.Tertiary.copy(alpha = 0.8f),
onTertiary = Color.White,
tertiaryContainer = ThemeConfig.currentTheme.TertiaryContainer.copy(alpha = 0.15f),
onTertiaryContainer = Color.White,
background = Color.Transparent,
surface = Color.Transparent,
onBackground = Color.White,
onSurface = Color.White,
surfaceVariant = Color(0xFF2F2F2F),
onSurfaceVariant = Color.White.copy(alpha = 0.7f),
outline = Color.White.copy(alpha = 0.12f),
outlineVariant = Color.White.copy(alpha = 0.12f),
error = ThemeConfig.currentTheme.Error,
onError = ThemeConfig.currentTheme.OnError,
errorContainer = ThemeConfig.currentTheme.ErrorContainer.copy(alpha = 0.15f),
onErrorContainer = Color.White
primary = ThemeConfig.currentTheme.primaryDark,
onPrimary = ThemeConfig.currentTheme.onPrimaryDark,
primaryContainer = ThemeConfig.currentTheme.primaryContainerDark,
onPrimaryContainer = ThemeConfig.currentTheme.onPrimaryContainerDark,
secondary = ThemeConfig.currentTheme.secondaryDark,
onSecondary = ThemeConfig.currentTheme.onSecondaryDark,
secondaryContainer = ThemeConfig.currentTheme.secondaryContainerDark,
onSecondaryContainer = ThemeConfig.currentTheme.onSecondaryContainerDark,
tertiary = ThemeConfig.currentTheme.tertiaryDark,
onTertiary = ThemeConfig.currentTheme.onTertiaryDark,
tertiaryContainer = ThemeConfig.currentTheme.tertiaryContainerDark,
onTertiaryContainer = ThemeConfig.currentTheme.onTertiaryContainerDark,
error = ThemeConfig.currentTheme.errorDark,
onError = ThemeConfig.currentTheme.onErrorDark,
errorContainer = ThemeConfig.currentTheme.errorContainerDark,
onErrorContainer = ThemeConfig.currentTheme.onErrorContainerDark,
background = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else ThemeConfig.currentTheme.backgroundDark,
onBackground = ThemeConfig.currentTheme.onBackgroundDark,
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else ThemeConfig.currentTheme.surfaceDark,
onSurface = ThemeConfig.currentTheme.onSurfaceDark,
surfaceVariant = ThemeConfig.currentTheme.surfaceVariantDark,
onSurfaceVariant = ThemeConfig.currentTheme.onSurfaceVariantDark,
outline = ThemeConfig.currentTheme.outlineDark,
outlineVariant = ThemeConfig.currentTheme.outlineVariantDark,
scrim = ThemeConfig.currentTheme.scrimDark,
inverseSurface = ThemeConfig.currentTheme.inverseSurfaceDark,
inverseOnSurface = ThemeConfig.currentTheme.inverseOnSurfaceDark,
inversePrimary = ThemeConfig.currentTheme.inversePrimaryDark,
surfaceDim = ThemeConfig.currentTheme.surfaceDimDark,
surfaceBright = ThemeConfig.currentTheme.surfaceBrightDark,
surfaceContainerLowest = ThemeConfig.currentTheme.surfaceContainerLowestDark,
surfaceContainerLow = ThemeConfig.currentTheme.surfaceContainerLowDark,
surfaceContainer = ThemeConfig.currentTheme.surfaceContainerDark,
surfaceContainerHigh = ThemeConfig.currentTheme.surfaceContainerHighDark,
surfaceContainerHighest = ThemeConfig.currentTheme.surfaceContainerHighestDark,
)
/**
@@ -342,32 +363,44 @@ private fun createDarkColorScheme() = darkColorScheme(
*/
@Composable
private fun createLightColorScheme() = lightColorScheme(
primary = ThemeConfig.currentTheme.Primary,
onPrimary = ThemeConfig.currentTheme.OnPrimary,
primaryContainer = ThemeConfig.currentTheme.PrimaryContainer,
onPrimaryContainer = ThemeConfig.currentTheme.OnPrimaryContainer,
secondary = ThemeConfig.currentTheme.Secondary,
onSecondary = ThemeConfig.currentTheme.OnSecondary,
secondaryContainer = ThemeConfig.currentTheme.SecondaryContainer,
onSecondaryContainer = ThemeConfig.currentTheme.OnSecondaryContainer,
tertiary = ThemeConfig.currentTheme.Tertiary,
onTertiary = ThemeConfig.currentTheme.OnTertiary,
tertiaryContainer = ThemeConfig.currentTheme.TertiaryContainer,
onTertiaryContainer = ThemeConfig.currentTheme.OnTertiaryContainer,
background = Color.Transparent,
surface = Color.Transparent,
onBackground = Color.Black.copy(alpha = 0.87f),
onSurface = Color.Black.copy(alpha = 0.87f),
surfaceVariant = Color(0xFFF5F5F5),
onSurfaceVariant = Color.Black.copy(alpha = 0.78f),
outline = Color.Black.copy(alpha = 0.12f),
outlineVariant = Color.Black.copy(alpha = 0.12f),
error = ThemeConfig.currentTheme.Error,
onError = ThemeConfig.currentTheme.OnError,
errorContainer = ThemeConfig.currentTheme.ErrorContainer,
onErrorContainer = ThemeConfig.currentTheme.OnErrorContainer
primary = ThemeConfig.currentTheme.primaryLight,
onPrimary = ThemeConfig.currentTheme.onPrimaryLight,
primaryContainer = ThemeConfig.currentTheme.primaryContainerLight,
onPrimaryContainer = ThemeConfig.currentTheme.onPrimaryContainerLight,
secondary = ThemeConfig.currentTheme.secondaryLight,
onSecondary = ThemeConfig.currentTheme.onSecondaryLight,
secondaryContainer = ThemeConfig.currentTheme.secondaryContainerLight,
onSecondaryContainer = ThemeConfig.currentTheme.onSecondaryContainerLight,
tertiary = ThemeConfig.currentTheme.tertiaryLight,
onTertiary = ThemeConfig.currentTheme.onTertiaryLight,
tertiaryContainer = ThemeConfig.currentTheme.tertiaryContainerLight,
onTertiaryContainer = ThemeConfig.currentTheme.onTertiaryContainerLight,
error = ThemeConfig.currentTheme.errorLight,
onError = ThemeConfig.currentTheme.onErrorLight,
errorContainer = ThemeConfig.currentTheme.errorContainerLight,
onErrorContainer = ThemeConfig.currentTheme.onErrorContainerLight,
background = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else ThemeConfig.currentTheme.backgroundLight,
onBackground = ThemeConfig.currentTheme.onBackgroundLight,
surface = if (CardConfig.isCustomBackgroundEnabled) Color.Transparent else ThemeConfig.currentTheme.surfaceLight,
onSurface = ThemeConfig.currentTheme.onSurfaceLight,
surfaceVariant = ThemeConfig.currentTheme.surfaceVariantLight,
onSurfaceVariant = ThemeConfig.currentTheme.onSurfaceVariantLight,
outline = ThemeConfig.currentTheme.outlineLight,
outlineVariant = ThemeConfig.currentTheme.outlineVariantLight,
scrim = ThemeConfig.currentTheme.scrimLight,
inverseSurface = ThemeConfig.currentTheme.inverseSurfaceLight,
inverseOnSurface = ThemeConfig.currentTheme.inverseOnSurfaceLight,
inversePrimary = ThemeConfig.currentTheme.inversePrimaryLight,
surfaceDim = ThemeConfig.currentTheme.surfaceDimLight,
surfaceBright = ThemeConfig.currentTheme.surfaceBrightLight,
surfaceContainerLowest = ThemeConfig.currentTheme.surfaceContainerLowestLight,
surfaceContainerLow = ThemeConfig.currentTheme.surfaceContainerLowLight,
surfaceContainer = ThemeConfig.currentTheme.surfaceContainerLight,
surfaceContainerHigh = ThemeConfig.currentTheme.surfaceContainerHighLight,
surfaceContainerHighest = ThemeConfig.currentTheme.surfaceContainerHighestLight,
)
/**
* 复制图片到应用内部存储并提升持久性
*/

View File

@@ -0,0 +1,105 @@
package com.sukisu.ultra.ui.util
import android.content.Context
import android.content.Intent
import android.net.Uri
import java.io.BufferedReader
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
import java.util.zip.ZipInputStream
import com.sukisu.ultra.R
import android.util.Log
import java.io.IOException
object ModuleUtils {
private const val TAG = "ModuleUtils"
fun extractModuleName(context: Context, uri: Uri): String {
if (uri == Uri.EMPTY) {
Log.e(TAG, "The supplied URI is empty")
return context.getString(R.string.unknown_module)
}
return try {
Log.d(TAG, "Start extracting module names from URIs: $uri")
// 从URI路径中提取文件名
val fileName = uri.lastPathSegment?.let { path ->
val lastSlash = path.lastIndexOf('/')
if (lastSlash != -1 && lastSlash < path.length - 1) {
path.substring(lastSlash + 1)
} else {
path
}
}?.removeSuffix(".zip") ?: context.getString(R.string.unknown_module)
var formattedFileName = fileName.replace(Regex("[^a-zA-Z0-9\\s\\-_.@()\\u4e00-\\u9fa5]"), "").trim()
var moduleName = formattedFileName
try {
// 打开ZIP文件输入流
val inputStream = context.contentResolver.openInputStream(uri)
if (inputStream == null) {
Log.e(TAG, "Unable to get input stream from URI: $uri")
return formattedFileName
}
val zipInputStream = ZipInputStream(inputStream)
var entry = zipInputStream.nextEntry
// 遍历ZIP文件中的条目查找module.prop文件
while (entry != null) {
if (entry.name == "module.prop") {
val reader = BufferedReader(InputStreamReader(zipInputStream, StandardCharsets.UTF_8))
var line: String?
var nameFound = false
while (reader.readLine().also { line = it } != null) {
if (line?.startsWith("name=") == true) {
moduleName = line.substringAfter("=")
moduleName = moduleName.replace(Regex("[^a-zA-Z0-9\\s\\-_.@()\\u4e00-\\u9fa5]"), "").trim()
nameFound = true
break
}
}
break
}
entry = zipInputStream.nextEntry
}
zipInputStream.close()
Log.d(TAG, "Successfully extracted module name: $moduleName")
moduleName
} catch (e: IOException) {
Log.e(TAG, "Error reading ZIP file: ${e.message}")
formattedFileName
}
} catch (e: Exception) {
Log.e(TAG, "Exception when extracting module name: ${e.message}")
context.getString(R.string.unknown_module)
}
}
// 验证URI是否有效并可访问
fun isUriAccessible(context: Context, uri: Uri): Boolean {
if (uri == Uri.EMPTY) return false
return try {
val inputStream = context.contentResolver.openInputStream(uri)
inputStream?.close()
inputStream != null
} catch (e: Exception) {
Log.e(TAG, "The URI is inaccessible: $uri, Error: ${e.message}")
false
}
}
// 获取URI的持久权限
fun takePersistableUriPermission(context: Context, uri: Uri) {
try {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flags)
Log.d(TAG, "Persistent permissions for URIs have been obtained: $uri")
} catch (e: Exception) {
Log.e(TAG, "Unable to get persistent permissions on URIs: $uri, Error: ${e.message}")
}
}
}

View File

@@ -0,0 +1,44 @@
package com.sukisu.ultra.ui.util
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import com.sukisu.ultra.ui.MainActivity
/**
* 重启应用程序
**/
fun Context.restartApp(
activityClass: Class<out Activity>,
finishCurrent: Boolean = true,
clearTask: Boolean = true,
newTask: Boolean = true
) {
val intent = Intent(this, activityClass)
if (clearTask) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
if (newTask) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
if (finishCurrent && this is Activity) {
finish()
}
}
/**
* 刷新启动器图标
*/
fun toggleLauncherIcon(context: Context, useAlt: Boolean) {
val pm = context.packageManager
val main = ComponentName(context, MainActivity::class.java.name)
val alt = ComponentName(context, "${MainActivity::class.java.name}Alias")
if (useAlt) {
pm.setComponentEnabledSetting(main, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
pm.setComponentEnabledSetting(alt, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
} else {
pm.setComponentEnabledSetting(alt, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
pm.setComponentEnabledSetting(main, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
}
}

View File

@@ -1,12 +1,10 @@
package com.sukisu.ultra.ui.util
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import android.content.Context
import com.topjohnwu.superuser.Shell
import com.sukisu.ultra.R
@Composable
fun getSELinuxStatus(): String {
fun getSELinuxStatus(context: Context): String {
val shell = Shell.Builder.create().build("sh")
val list = ArrayList<String>()
@@ -18,16 +16,16 @@ fun getSELinuxStatus(): String {
return if (result.isSuccess) {
when (output) {
"Enforcing" -> stringResource(R.string.selinux_status_enforcing)
"Permissive" -> stringResource(R.string.selinux_status_permissive)
"Disabled" -> stringResource(R.string.selinux_status_disabled)
else -> stringResource(R.string.selinux_status_unknown)
"Enforcing" -> context.getString(R.string.selinux_status_enforcing)
"Permissive" -> context.getString(R.string.selinux_status_permissive)
"Disabled" -> context.getString(R.string.selinux_status_disabled)
else -> context.getString(R.string.selinux_status_unknown)
}
} else {
if (output.contains("Permission denied")) {
stringResource(R.string.selinux_status_enforcing)
context.getString(R.string.selinux_status_enforcing)
} else {
stringResource(R.string.selinux_status_unknown)
context.getString(R.string.selinux_status_unknown)
}
}
}

View File

@@ -0,0 +1,285 @@
package com.sukisu.ultra.ui.viewmodel
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.system.Os
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dergoogler.mmrl.platform.Platform.Companion.context
import com.google.gson.Gson
import com.sukisu.ultra.KernelVersion
import com.sukisu.ultra.Natives
import com.sukisu.ultra.getKernelVersion
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.util.*
import com.sukisu.ultra.ui.util.module.LatestVersionInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import androidx.core.content.edit
class HomeViewModel : ViewModel() {
companion object {
private const val TAG = "HomeViewModel"
private const val PREFS_NAME = "home_cache"
private const val KEY_SYSTEM_STATUS = "system_status"
private const val KEY_SYSTEM_INFO = "system_info"
private const val KEY_VERSION_INFO = "version_info"
private const val KEY_LAST_UPDATE = "last_update_time"
}
// 系统状态
data class SystemStatus(
val isManager: Boolean = false,
val ksuVersion: Int? = null,
val lkmMode: Boolean? = null,
val kernelVersion: KernelVersion = getKernelVersion(),
val isRootAvailable: Boolean = false,
val isKpmConfigured: Boolean = false,
val requireNewKernel: Boolean = false
)
// 系统信息
data class SystemInfo(
val kernelRelease: String = "",
val androidVersion: String = "",
val deviceModel: String = "",
val managerVersion: Pair<String, Long> = Pair("", 0L),
val seLinuxStatus: String = "",
val kpmVersion: String = "",
val suSFSStatus: String = "",
val suSFSVersion: String = "",
val suSFSVariant: String = "",
val suSFSFeatures: String = "",
val susSUMode: String = "",
val superuserCount: Int = 0,
val moduleCount: Int = 0,
val kpmModuleCount: Int = 0
)
private val gson = Gson()
private val prefs by lazy { ksuApp.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) }
var systemStatus by mutableStateOf(SystemStatus())
private set
var systemInfo by mutableStateOf(SystemInfo())
private set
var latestVersionInfo by mutableStateOf(LatestVersionInfo())
private set
var isSimpleMode by mutableStateOf(false)
private set
var isHideVersion by mutableStateOf(false)
private set
var isHideOtherInfo by mutableStateOf(false)
private set
var isHideSusfsStatus by mutableStateOf(false)
private set
var isHideLinkCard by mutableStateOf(false)
private set
var showKpmInfo by mutableStateOf(false)
private set
fun loadUserSettings(context: Context) {
viewModelScope.launch(Dispatchers.IO) {
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
isSimpleMode = prefs.getBoolean("is_simple_mode", false)
isHideVersion = prefs.getBoolean("is_hide_version", false)
isHideOtherInfo = prefs.getBoolean("is_hide_other_info", false)
isHideSusfsStatus = prefs.getBoolean("is_hide_susfs_status", false)
isHideLinkCard = prefs.getBoolean("is_hide_link_card", false)
showKpmInfo = prefs.getBoolean("show_kpm_info", false)
}
}
fun initializeData() {
viewModelScope.launch {
loadCachedData()
}
}
private fun loadCachedData() {
prefs.getString(KEY_SYSTEM_STATUS, null)?.let {
systemStatus = gson.fromJson(it, SystemStatus::class.java)
}
prefs.getString(KEY_SYSTEM_INFO, null)?.let {
systemInfo = gson.fromJson(it, SystemInfo::class.java)
}
prefs.getString(KEY_VERSION_INFO, null)?.let {
latestVersionInfo = gson.fromJson(it, LatestVersionInfo::class.java)
}
}
private suspend fun fetchAndSaveData() {
fetchSystemStatus()
fetchSystemInfo()
withContext(Dispatchers.IO) {
prefs.edit {
putString(KEY_SYSTEM_STATUS, gson.toJson(systemStatus))
putString(KEY_SYSTEM_INFO, gson.toJson(systemInfo))
putString(KEY_VERSION_INFO, gson.toJson(latestVersionInfo))
putLong(KEY_LAST_UPDATE, System.currentTimeMillis())
}
}
}
fun checkForUpdates(context: Context) {
viewModelScope.launch(Dispatchers.IO) {
try {
val checkUpdate = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
.getBoolean("check_update", true)
if (checkUpdate) {
val newVersionInfo = checkNewVersion()
latestVersionInfo = newVersionInfo
prefs.edit {
putString(KEY_VERSION_INFO, gson.toJson(newVersionInfo))
putLong(KEY_LAST_UPDATE, System.currentTimeMillis())
}
}
} catch (e: Exception) {
Log.e(TAG, "Error checking for updates", e)
}
}
}
fun refreshAllData(context: Context) {
viewModelScope.launch {
try {
fetchAndSaveData()
checkForUpdates(context)
} catch (e: Exception) {
Log.e(TAG, "Error refreshing data", e)
}
}
}
private suspend fun fetchSystemStatus() {
withContext(Dispatchers.IO) {
try {
val kernelVersion = getKernelVersion()
val isManager = Natives.becomeManager(ksuApp.packageName)
val ksuVersion = if (isManager) Natives.version else null
val lkmMode = ksuVersion?.let {
if (it >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && kernelVersion.isGKI()) Natives.isLkmMode else null
}
systemStatus = SystemStatus(
isManager = isManager,
ksuVersion = ksuVersion,
lkmMode = lkmMode,
kernelVersion = kernelVersion,
isRootAvailable = rootAvailable(),
isKpmConfigured = Natives.isKPMEnabled(),
requireNewKernel = isManager && Natives.requireNewKernel()
)
} catch (e: Exception) {
Log.e(TAG, "Error fetching system status", e)
}
}
}
@SuppressLint("RestrictedApi")
private suspend fun fetchSystemInfo() {
withContext(Dispatchers.IO) {
try {
val uname = Os.uname()
val kpmVersion = getKpmVersion()
val suSFS = getSuSFS()
var suSFSVersion = ""
var suSFSVariant = ""
var suSFSFeatures = ""
var susSUMode = ""
if (suSFS == "Supported") {
suSFSVersion = getSuSFSVersion()
if (suSFSVersion.isNotEmpty()) {
suSFSVariant = getSuSFSVariant()
suSFSFeatures = getSuSFSFeatures()
val isSUS_SU = suSFSFeatures == "CONFIG_KSU_SUSFS_SUS_SU"
if (isSUS_SU) {
susSUMode = try {
susfsSUS_SU_Mode().toString()
} catch (_: Exception) {
""
}
}
}
}
systemInfo = SystemInfo(
kernelRelease = uname.release,
androidVersion = Build.VERSION.RELEASE,
deviceModel = getDeviceModel(),
managerVersion = getManagerVersion(ksuApp.applicationContext),
seLinuxStatus = getSELinuxStatus(context),
kpmVersion = kpmVersion,
suSFSStatus = suSFS,
suSFSVersion = suSFSVersion,
suSFSVariant = suSFSVariant,
suSFSFeatures = suSFSFeatures,
susSUMode = susSUMode,
superuserCount = getSuperuserCount(),
moduleCount = getModuleCount(),
kpmModuleCount = getKpmModuleCount()
)
} catch (e: Exception) {
Log.e(TAG, "Error fetching system info", e)
}
}
}
private fun getDeviceInfo(): String {
var manufacturer =
Build.MANUFACTURER[0].uppercaseChar().toString() + Build.MANUFACTURER.substring(1)
if (Build.BRAND != Build.MANUFACTURER) {
manufacturer += " " + Build.BRAND[0].uppercaseChar() + Build.BRAND.substring(1)
}
manufacturer += " " + Build.MODEL + " "
return manufacturer
}
@SuppressLint("PrivateApi")
private fun getDeviceModel(): String {
return try {
val systemProperties = Class.forName("android.os.SystemProperties")
val getMethod = systemProperties.getMethod("get", String::class.java, String::class.java)
val marketNameKeys = listOf(
"ro.product.marketname", // Xiaomi
"ro.vendor.oplus.market.name", // Oppo, OnePlus, Realme
"ro.vivo.market.name", // Vivo
"ro.config.marketing_name" // Huawei
)
var result = getDeviceInfo()
for (key in marketNameKeys) {
val marketName = getMethod.invoke(null, key, "") as String
if (marketName.isNotEmpty()) {
result = marketName
break
}
}
result
} catch (e: Exception) {
Log.e(TAG, "Error getting device model", e)
getDeviceInfo()
}
}
private fun getManagerVersion(context: Context): Pair<String, Long> {
return try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)!!
val versionCode = androidx.core.content.pm.PackageInfoCompat.getLongVersionCode(packageInfo)
Pair(packageInfo.versionName!!, versionCode)
} catch (e: Exception) {
Log.e(TAG, "Error getting manager version", e)
Pair("", 0L)
}
}
}

View File

@@ -11,6 +11,10 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.sukisu.ultra.ui.util.*
/**
* @author ShirkNeko
* @date 2025/5/31.
*/
class KpmViewModel : ViewModel() {
var moduleList by mutableStateOf(emptyList<ModuleInfo>())
private set

View File

@@ -8,16 +8,23 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dergoogler.mmrl.platform.model.ModuleConfig
import com.dergoogler.mmrl.platform.model.ModuleConfig.Companion.asModuleConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.sukisu.ultra.ui.util.HanziToPinyin
import com.sukisu.ultra.ui.util.listModules
import kotlinx.coroutines.withContext
import org.json.JSONArray
import org.json.JSONObject
import java.text.Collator
import java.util.Locale
import java.util.concurrent.TimeUnit
/**
* @author ShirkNeko
* @date 2025/5/31.
*/
class ModuleViewModel : ViewModel() {
companion object {
@@ -40,6 +47,7 @@ class ModuleViewModel : ViewModel() {
val hasWebUi: Boolean,
val hasActionScript: Boolean,
val dirId: String, // real module id (dir name)
var config: ModuleConfig? = null,
)
var isRefreshing by mutableStateOf(false)
@@ -100,9 +108,42 @@ class ModuleViewModel : ViewModel() {
obj.optString("updateJson"),
obj.optBoolean("web"),
obj.optBoolean("action"),
obj.getString("dir_id"),
obj.getString("dir_id")
)
}.toList()
launch {
modules.forEach { module ->
withContext(Dispatchers.IO) {
try {
runCatching {
module.config = module.id.asModuleConfig
}.onFailure { e ->
Log.e(TAG, "Failed to load config from id for module ${module.id}", e)
}
if (module.config == null) {
runCatching {
module.config = module.name.asModuleConfig
}.onFailure { e ->
Log.e(TAG, "Failed to load config from name for module ${module.id}", e)
}
}
if (module.config == null) {
runCatching {
module.config = module.description.asModuleConfig
}.onFailure { e ->
Log.e(TAG, "Failed to load config from description for module ${module.id}", e)
}
}
if (module.config == null) {
module.config = ModuleConfig()
}
} catch (e: Exception) {
Log.e(TAG, "Failed to load any config for module ${module.id}", e)
module.config = ModuleConfig()
}
}
}
}
isNeedRefresh = false
}.onFailure { e ->
Log.e(TAG, "fetchModuleList: ", e)

View File

@@ -1,10 +1,12 @@
package com.sukisu.ultra.ui.viewmodel
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.Parcelable
import android.os.SystemClock
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -20,14 +22,21 @@ import java.text.Collator
import java.util.*
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.TIMEOUT_MILLIS
import com.sukisu.ultra.ui.webui.getInstalledPackagesAll
import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeoutOrNull
import androidx.core.content.edit
/**
* @author ShirkNeko
* @date 2025/5/31.
*/
class SuperUserViewModel : ViewModel() {
val isPlatformAlive get() = Platform.isAlive
companion object {
private const val TAG = "SuperUserViewModel"
private var apps by mutableStateOf<List<AppInfo>>(emptyList())
var apps by mutableStateOf<List<AppInfo>>(emptyList())
}
@Parcelize
@@ -55,9 +64,11 @@ class SuperUserViewModel : ViewModel() {
}
}
}
private val prefs = ksuApp.getSharedPreferences("settings", Context.MODE_PRIVATE)!!
var search by mutableStateOf("")
var showSystemApps by mutableStateOf(false)
var showSystemApps by mutableStateOf(prefs.getBoolean("show_system_apps", false))
private set
var isRefreshing by mutableStateOf(false)
private set
@@ -67,6 +78,11 @@ class SuperUserViewModel : ViewModel() {
var selectedApps by mutableStateOf<Set<String>>(emptySet())
internal set
fun updateShowSystemApps(newValue: Boolean) {
showSystemApps = newValue
prefs.edit { putBoolean("show_system_apps", newValue) }
}
private val sortedList by derivedStateOf {
val comparator = compareBy<AppInfo> {
when {
@@ -164,7 +180,7 @@ class SuperUserViewModel : ViewModel() {
fetchAppList() // 刷新列表以显示最新状态
}
// 更新本地应用配置,避免重新获取整个列表导致滚动位置重置
// 更新本地应用配置
fun updateAppProfileLocally(packageName: String, updatedProfile: Natives.Profile) {
apps = apps.map { app ->
if (app.packageName == packageName) {
@@ -187,15 +203,10 @@ class SuperUserViewModel : ViewModel() {
val pm = ksuApp.packageManager
val start = SystemClock.elapsedRealtime()
val userInfos = Platform.userManager.getUsers()
val packages = mutableListOf<PackageInfo>()
val packageManager = Platform.packageManager
for (userInfo in userInfos) {
Log.i(TAG, "fetchAppList: ${userInfo.id}")
packages.addAll(packageManager.getInstalledPackages(0, userInfo.id))
val packages = Platform.getInstalledPackagesAll {
Log.e(TAG, "getInstalledPackagesAll:", it)
Toast.makeText(ksuApp, "Something went wrong, check logs", Toast.LENGTH_SHORT).show()
}
apps = packages.map {
val appInfo = it.applicationInfo
val uid = appInfo!!.uid

View File

@@ -2,6 +2,7 @@ package com.sukisu.ultra.ui.webui
import android.content.ServiceConnection
import android.util.Log
import android.content.pm.PackageInfo
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.model.IProvider
import com.dergoogler.mmrl.platform.model.PlatformIntent
@@ -54,3 +55,18 @@ suspend fun initPlatform() = withContext(Dispatchers.IO) {
return@withContext false
}
}
fun Platform.Companion.getInstalledPackagesAll(catch: (Exception) -> Unit = {}): List<PackageInfo> =
try {
val packages = mutableListOf<PackageInfo>()
val userInfos = userManager.getUsers()
for (userInfo in userInfos) {
packages.addAll(packageManager.getInstalledPackages(0, userInfo.id))
}
packages
} catch (e: Exception) {
catch(e)
packageManager.getInstalledPackages(0, userManager.myUserId)
}

View File

@@ -1,273 +0,0 @@
package com.sukisu.ultra.ui.webui
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.paint
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.zIndex
import android.os.Build
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.updateTransition
import androidx.compose.ui.graphics.graphicsLayer
import coil.compose.AsyncImagePainter
import coil.compose.rememberAsyncImagePainter
import com.sukisu.ultra.ui.theme.ThemeConfig
import com.sukisu.ultra.ui.theme.Typography
import com.sukisu.ultra.ui.theme.loadCustomBackground
// 提供界面类型的本地组合
val LocalIsSecondaryScreen = staticCompositionLocalOf { false }
/**
* WebUI专用主题配置
*/
@Composable
fun WebUIXTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
isSecondaryScreen: Boolean = false,
content: @Composable () -> Unit
) {
val context = LocalContext.current
LaunchedEffect(Unit) {
if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) {
context.loadCustomBackground()
ThemeConfig.backgroundImageLoaded = false
}
}
// 更新二级界面状态
LaunchedEffect(isSecondaryScreen) {
WebViewInterface.updateSecondaryScreenState(isSecondaryScreen)
}
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
if (darkTheme) {
dynamicDarkColorScheme(context).let { scheme ->
if (isSecondaryScreen) {
scheme.copy(
background = scheme.surfaceContainerHighest,
surface = scheme.surfaceContainerHighest
)
} else {
scheme.copy(
background = Color.Transparent,
surface = Color.Transparent
)
}
}
} else {
dynamicLightColorScheme(context).let { scheme ->
if (isSecondaryScreen) {
scheme.copy(
background = scheme.surfaceContainerHighest,
surface = scheme.surfaceContainerHighest
)
} else {
scheme.copy(
background = Color.Transparent,
surface = Color.Transparent
)
}
}
}
}
darkTheme -> {
if (isSecondaryScreen) {
darkColorScheme().copy(
background = MaterialTheme.colorScheme.surfaceContainerHighest,
surface = MaterialTheme.colorScheme.surfaceContainerHighest
)
} else {
darkColorScheme().copy(
background = Color.Transparent,
surface = Color.Transparent
)
}
}
else -> {
if (isSecondaryScreen) {
lightColorScheme().copy(
background = MaterialTheme.colorScheme.surfaceContainerHighest,
surface = MaterialTheme.colorScheme.surfaceContainerHighest
)
} else {
lightColorScheme().copy(
background = Color.Transparent,
surface = Color.Transparent
)
}
}
}
ConfigureSystemBars(darkTheme)
val backgroundUri = remember { mutableStateOf(ThemeConfig.customBackgroundUri) }
LaunchedEffect(ThemeConfig.customBackgroundUri) {
backgroundUri.value = ThemeConfig.customBackgroundUri
}
val bgImagePainter = backgroundUri.value?.let {
rememberAsyncImagePainter(
model = it,
onError = {
ThemeConfig.backgroundImageLoaded = false
},
onSuccess = {
ThemeConfig.backgroundImageLoaded = true
ThemeConfig.isThemeChanging = false
}
)
}
// 背景透明度动画
val transition = updateTransition(
targetState = ThemeConfig.backgroundImageLoaded,
label = "bgTransition"
)
val bgAlpha by transition.animateFloat(
label = "bgAlpha",
transitionSpec = {
spring(
dampingRatio = 0.8f,
stiffness = 300f
)
}
) { loaded -> if (loaded) 1f else 0f }
CompositionLocalProvider(LocalIsSecondaryScreen provides isSecondaryScreen) {
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
) {
if (isSecondaryScreen) {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.surfaceContainerHighest)
) {
content()
}
} else {
Box(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.fillMaxSize()
.zIndex(-2f)
.background(if (darkTheme) Color.Black else Color.White)
)
backgroundUri.value?.let { uri ->
Box(
modifier = Modifier
.fillMaxSize()
.zIndex(-1f)
.alpha(bgAlpha)
) {
bgImagePainter?.let { painter ->
Box(
modifier = Modifier
.fillMaxSize()
.paint(
painter = painter,
contentScale = ContentScale.Crop
)
.graphicsLayer {
alpha = (painter.state as? AsyncImagePainter.State.Success)?.let { 1f } ?: 0f
}
)
}
Box(
modifier = Modifier
.fillMaxSize()
.background(
if (darkTheme) Color.Black.copy(alpha = 0.6f)
else Color.White.copy(alpha = 0.1f)
)
)
Box(
modifier = Modifier
.fillMaxSize()
.background(
Brush.radialGradient(
colors = listOf(
Color.Transparent,
if (darkTheme) Color.Black.copy(alpha = 0.5f)
else Color.Black.copy(alpha = 0.2f)
),
radius = 1200f
)
)
)
}
}
Box(
modifier = Modifier
.fillMaxSize()
.zIndex(1f)
) {
content()
}
}
}
}
}
}
/**
* 配置WebUI的系统栏样式
*/
@Composable
private fun ConfigureSystemBars(
darkMode: Boolean,
statusBarScrim: Color = Color.Transparent,
navigationBarScrim: Color = Color.Transparent
) {
val context = LocalContext.current
val activity = context as ComponentActivity
SideEffect {
activity.enableEdgeToEdge(
statusBarStyle = SystemBarStyle.auto(
statusBarScrim.toArgb(),
statusBarScrim.toArgb()
) { darkMode },
navigationBarStyle = when {
darkMode -> SystemBarStyle.dark(
navigationBarScrim.toArgb()
)
else -> SystemBarStyle.light(
navigationBarScrim.toArgb(),
navigationBarScrim.toArgb()
)
}
)
}
}

View File

@@ -20,6 +20,7 @@ import com.dergoogler.mmrl.ui.component.Loading
import com.dergoogler.mmrl.webui.screen.WebUIScreen
import com.dergoogler.mmrl.webui.util.rememberWebUIOptions
import com.sukisu.ultra.BuildConfig
import com.sukisu.ultra.ui.theme.KernelSUTheme
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -68,7 +69,7 @@ class WebUIXActivity : ComponentActivity() {
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
setContent {
WebUIXTheme {
KernelSUTheme {
var isLoading by remember { mutableStateOf(true) }
LaunchedEffect(Platform.isAlive) {
@@ -81,7 +82,7 @@ class WebUIXActivity : ComponentActivity() {
if (isLoading) {
Loading()
return@WebUIXTheme
return@KernelSUTheme
}
val webDebugging = prefs.getBoolean("enable_web_debugging", false)

View File

@@ -7,9 +7,6 @@ import android.text.TextUtils
import android.view.Window
import android.webkit.JavascriptInterface
import android.widget.Toast
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.dergoogler.mmrl.webui.interfaces.WXInterface
@@ -23,8 +20,7 @@ import com.sukisu.ultra.ui.util.listModules
import com.sukisu.ultra.ui.util.withNewRootShell
import org.json.JSONArray
import org.json.JSONObject
import com.sukisu.ultra.ui.util.controlKpmModule
import com.sukisu.ultra.ui.util.listKpmModules
import com.sukisu.ultra.ui.util.*
import java.io.File
import java.util.concurrent.CompletableFuture
@@ -34,47 +30,11 @@ class WebViewInterface(
override var name: String = "ksu"
companion object {
private var isSecondaryScreenState by mutableStateOf(false)
private var windowInsetsController: WindowInsetsControllerCompat? = null
fun factory() = JavaScriptInterface(WebViewInterface::class.java)
fun updateSecondaryScreenState(isSecondary: Boolean) {
isSecondaryScreenState = isSecondary
windowInsetsController?.let { controller ->
if (isSecondary) {
controller.show(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_DEFAULT
} else {
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
}
fun setWindowInsetsController(controller: WindowInsetsControllerCompat) {
windowInsetsController = controller
}
}
init {
if (context is Activity) {
setWindowInsetsController(WindowInsetsControllerCompat(
activity.window,
activity.window.decorView
))
}
}
private val modDir get() = "/data/adb/modules/${modId.id}"
@JavascriptInterface
fun isSecondaryPage(): Boolean {
return isSecondaryScreenState
}
@JavascriptInterface
fun exec(cmd: String): String {
return withNewRootShell(true) { ShellUtils.fastCmd(this, cmd) }
@@ -226,7 +186,7 @@ class WebViewInterface(
@JavascriptInterface
fun moduleInfo(): String {
val moduleInfos = JSONArray(listModules())
var currentModuleInfo = JSONObject()
val currentModuleInfo = JSONObject()
currentModuleInfo.put("moduleDir", modDir)
val moduleId = File(modDir).getName()
for (i in 0 until moduleInfos.length()) {
@@ -236,7 +196,7 @@ class WebViewInterface(
continue
}
var keys = currentInfo.keys()
val keys = currentInfo.keys()
for (key in keys) {
currentModuleInfo.put(key, currentInfo.get(key))
}

View File

@@ -0,0 +1,222 @@
package zako.zako.zako.zakoui.activity.component
import android.annotation.SuppressLint
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavHostController
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
import com.ramcosta.composedestinations.spec.RouteOrDirection
import com.ramcosta.composedestinations.generated.NavGraphs
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.MainActivity
import zako.zako.zako.zakoui.activity.util.AppData
import zako.zako.zako.zakoui.activity.util.AppData.getKpmVersionUse
import com.sukisu.ultra.ui.screen.BottomBarDestination
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.navigationBars
import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager
@SuppressLint("ContextCastToActivity")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomBar(navController: NavHostController) {
val navigator = navController.rememberDestinationsNavigator()
val isFullFeatured = AppData.isFullFeatured(ksuApp.packageName)
val kpmVersion = getKpmVersionUse()
val cardColor = MaterialTheme.colorScheme.surfaceContainer
val activity = LocalContext.current as MainActivity
val settings by activity.settingsStateFlow.collectAsState()
// 检查是否隐藏红点
val isHideOtherInfo = settings.isHideOtherInfo
val showKpmInfo = settings.showKpmInfo
// 收集计数数据
val superuserCount by DataRefreshManager.superuserCount.collectAsState()
val moduleCount by DataRefreshManager.moduleCount.collectAsState()
val kpmModuleCount by DataRefreshManager.kpmModuleCount.collectAsState()
NavigationBar(
modifier = Modifier.windowInsetsPadding(
WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal)
),
containerColor = TopAppBarDefaults.topAppBarColors(
containerColor = cardColor.copy(alpha = cardAlpha),
scrolledContainerColor = cardColor.copy(alpha = cardAlpha)
).containerColor,
tonalElevation = cardElevation
) {
BottomBarDestination.entries.forEach { destination ->
if (destination == BottomBarDestination.Kpm) {
if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error") && !showKpmInfo && Natives.version >= Natives.MINIMAL_SUPPORTED_KPM) {
if (!isFullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem(
selected = isCurrentDestOnBackStack,
onClick = {
if (!isCurrentDestOnBackStack) {
navigator.popBackStack(destination.direction, false)
}
navigator.navigate(destination.direction) {
popUpTo(NavGraphs.root as RouteOrDirection) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
BadgedBox(
badge = {
if (kpmModuleCount > 0 && !isHideOtherInfo) {
Badge(
containerColor = MaterialTheme.colorScheme.secondary
) {
Text(
text = kpmModuleCount.toString(),
style = MaterialTheme.typography.labelSmall
)
}
}
}
) {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
}
},
label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) },
alwaysShowLabel = false
)
}
} else if (destination == BottomBarDestination.SuperUser) {
if (!isFullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem(
selected = isCurrentDestOnBackStack,
onClick = {
if (isCurrentDestOnBackStack) {
navigator.popBackStack(destination.direction, false)
}
navigator.navigate(destination.direction) {
popUpTo(NavGraphs.root) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
BadgedBox(
badge = {
if (superuserCount > 0 && !isHideOtherInfo) {
Badge(
containerColor = MaterialTheme.colorScheme.secondary
) {
Text(
text = superuserCount.toString(),
style = MaterialTheme.typography.labelSmall
)
}
}
}
) {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
}
},
label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) },
alwaysShowLabel = false
)
} else if (destination == BottomBarDestination.Module) {
if (!isFullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem(
selected = isCurrentDestOnBackStack,
onClick = {
if (isCurrentDestOnBackStack) {
navigator.popBackStack(destination.direction, false)
}
navigator.navigate(destination.direction) {
popUpTo(NavGraphs.root) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
BadgedBox(
badge = {
if (moduleCount > 0 && !isHideOtherInfo) {
Badge(
containerColor = MaterialTheme.colorScheme.secondary)
{
Text(
text = moduleCount.toString(),
style = MaterialTheme.typography.labelSmall
)
}
}
}
) {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
}
},
label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) },
alwaysShowLabel = false
)
} else {
if (!isFullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem(
selected = isCurrentDestOnBackStack,
onClick = {
if (isCurrentDestOnBackStack) {
navigator.popBackStack(destination.direction, false)
}
navigator.navigate(destination.direction) {
popUpTo(NavGraphs.root) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
},
label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) },
alwaysShowLabel = false
)
}
}
}
}

View File

@@ -0,0 +1,24 @@
package zako.zako.zako.zakoui.activity.util
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.runtime.Composable
object AnimatedBottomBar {
@Composable
fun AnimatedBottomBarWrapper(
showBottomBar: Boolean,
content: @Composable () -> Unit
) {
AnimatedVisibility(
visible = showBottomBar,
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut()
) {
content()
}
}
}

View File

@@ -0,0 +1,109 @@
package zako.zako.zako.zakoui.activity.util
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.util.getKpmModuleCount
import com.sukisu.ultra.ui.util.getKpmVersion
import com.sukisu.ultra.ui.util.getModuleCount
import com.sukisu.ultra.ui.util.getSuperuserCount
import com.sukisu.ultra.ui.util.rootAvailable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.withContext
object AppData {
object DataRefreshManager {
// 私有状态流
private val _superuserCount = MutableStateFlow(0)
private val _moduleCount = MutableStateFlow(0)
private val _kpmModuleCount = MutableStateFlow(0)
// 公开的只读状态流
val superuserCount: StateFlow<Int> = _superuserCount.asStateFlow()
val moduleCount: StateFlow<Int> = _moduleCount.asStateFlow()
val kpmModuleCount: StateFlow<Int> = _kpmModuleCount.asStateFlow()
/**
* 刷新所有数据计数
*/
fun refreshData() {
_superuserCount.value = getSuperuserCountUse()
_moduleCount.value = getModuleCountUse()
_kpmModuleCount.value = getKpmModuleCountUse()
}
/**
* 异步刷新所有数据
*/
suspend fun refreshDataAsync() = withContext(Dispatchers.IO) {
refreshData()
}
}
/**
* 获取超级用户应用计数
*/
fun getSuperuserCountUse(): Int {
return try {
if (!rootAvailable()) return 0
getSuperuserCount()
} catch (_: Exception) {
0
}
}
/**
* 获取模块计数
*/
fun getModuleCountUse(): Int {
return try {
if (!rootAvailable()) return 0
getModuleCount()
} catch (_: Exception) {
0
}
}
/**
* 获取KPM模块计数
*/
fun getKpmModuleCountUse(): Int {
return try {
if (!rootAvailable()) return 0
val kpmVersion = getKpmVersionUse()
if (kpmVersion.isEmpty() || kpmVersion.startsWith("Error")) return 0
getKpmModuleCount()
} catch (_: Exception) {
0
}
}
/**
* 获取KPM版本
*/
fun getKpmVersionUse(): String {
return try {
if (!rootAvailable()) return ""
val version = getKpmVersion()
if (version.isEmpty()) "" else version
} catch (e: Exception) {
"Error: ${e.message}"
}
}
/**
* 检查是否具有管理员权限
*/
fun isManager(packageName: String): Boolean {
return Natives.becomeManager(packageName)
}
/**
* 检查是否是完整功能模式
*/
fun isFullFeatured(packageName: String): Boolean {
val isManager = Natives.becomeManager(packageName)
return isManager && !Natives.requireNewKernel() && rootAvailable()
}
}

View File

@@ -0,0 +1,46 @@
package zako.zako.zako.zakoui.activity.util
import android.content.Context
import androidx.lifecycle.LifecycleCoroutineScope
import com.sukisu.ultra.ui.MainActivity
import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
object DataRefreshUtils {
fun startDataRefreshCoroutine(scope: LifecycleCoroutineScope) {
scope.launch(Dispatchers.IO) {
while (isActive) {
DataRefreshManager.refreshData()
delay(5000)
}
}
}
fun startSettingsMonitorCoroutine(
scope: LifecycleCoroutineScope,
activity: MainActivity,
settingsStateFlow: MutableStateFlow<MainActivity.SettingsState>
) {
scope.launch(Dispatchers.IO) {
while (isActive) {
val prefs = activity.getSharedPreferences("settings", Context.MODE_PRIVATE)
settingsStateFlow.value = MainActivity.SettingsState(
isHideOtherInfo = prefs.getBoolean("is_hide_other_info", false),
showKpmInfo = prefs.getBoolean("show_kpm_info", false)
)
delay(1000)
}
}
}
fun refreshData(scope: LifecycleCoroutineScope) {
scope.launch {
DataRefreshManager.refreshData()
}
}
}

View File

@@ -0,0 +1,24 @@
package zako.zako.zako.zakoui.activity.util
import android.content.Context
object DisplayUtils {
fun applyCustomDpi(context: Context) {
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val customDpi = prefs.getInt("app_dpi", 0)
if (customDpi > 0) {
try {
val resources = context.resources
val metrics = resources.displayMetrics
metrics.density = customDpi / 160f
@Suppress("DEPRECATION")
metrics.scaledDensity = customDpi / 160f
metrics.densityDpi = customDpi
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}

View File

@@ -0,0 +1,48 @@
package zako.zako.zako.zakoui.activity.util
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import java.util.Locale
object LocaleUtils {
@SuppressLint("ObsoleteSdkInt")
fun applyLanguageSetting(context: Context) {
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val languageCode = prefs.getString("app_language", "") ?: ""
if (languageCode.isNotEmpty()) {
val locale = Locale.forLanguageTag(languageCode)
Locale.setDefault(locale)
val resources = context.resources
val config = Configuration(resources.configuration)
config.setLocale(locale)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context.createConfigurationContext(config)
} else {
@Suppress("DEPRECATION")
resources.updateConfiguration(config, resources.displayMetrics)
}
}
}
fun applyLocale(context: Context): Context {
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val languageCode = prefs.getString("app_language", "") ?: ""
var newContext = context
if (languageCode.isNotEmpty()) {
val locale = Locale.forLanguageTag(languageCode)
Locale.setDefault(locale)
val config = Configuration(context.resources.configuration)
config.setLocale(locale)
newContext = context.createConfigurationContext(config)
}
return newContext
}
}

View File

@@ -0,0 +1,19 @@
package zako.zako.zako.zakoui.activity.util
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.navigation.NavBackStackEntry
import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle
object NavigationUtils {
fun defaultTransitions() = object : NavHostAnimatedDestinationStyle() {
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition
get() = { fadeIn(animationSpec = tween(340)) }
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition
get() = { fadeOut(animationSpec = tween(340)) }
}
}

View File

@@ -0,0 +1,96 @@
package zako.zako.zako.zakoui.activity.util
import android.content.Context
import android.database.ContentObserver
import android.os.Handler
import androidx.core.content.edit
import com.sukisu.ultra.ui.MainActivity
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.theme.ThemeConfig
import kotlinx.coroutines.flow.MutableStateFlow
class ThemeChangeContentObserver(
handler: Handler,
private val onThemeChanged: () -> Unit
) : ContentObserver(handler) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
onThemeChanged()
}
}
object ThemeUtils {
fun initializeThemeSettings(activity: MainActivity, settingsStateFlow: MutableStateFlow<MainActivity.SettingsState>) {
val prefs = activity.getSharedPreferences("settings", Context.MODE_PRIVATE)
val isFirstRun = prefs.getBoolean("is_first_run", true)
settingsStateFlow.value = MainActivity.SettingsState(
isHideOtherInfo = prefs.getBoolean("is_hide_other_info", false),
showKpmInfo = prefs.getBoolean("show_kpm_info", false)
)
if (isFirstRun) {
ThemeConfig.preventBackgroundRefresh = false
activity.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE).edit {
putBoolean("prevent_background_refresh", false)
}
prefs.edit { putBoolean("is_first_run", false) }
}
// 加载保存的背景设置
loadThemeMode()
loadThemeColors()
loadDynamicColorState()
CardConfig.load(activity.applicationContext)
}
fun registerThemeChangeObserver(activity: MainActivity): ThemeChangeContentObserver {
val contentObserver = ThemeChangeContentObserver(Handler(activity.mainLooper)) {
activity.runOnUiThread {
if (!ThemeConfig.preventBackgroundRefresh) {
ThemeConfig.backgroundImageLoaded = false
loadCustomBackground()
}
}
}
activity.contentResolver.registerContentObserver(
android.provider.Settings.System.getUriFor("ui_night_mode"),
false,
contentObserver
)
return contentObserver
}
fun unregisterThemeChangeObserver(activity: MainActivity, observer: ThemeChangeContentObserver) {
activity.contentResolver.unregisterContentObserver(observer)
}
fun onActivityPause(activity: MainActivity) {
CardConfig.save(activity.applicationContext)
activity.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE).edit {
putBoolean("prevent_background_refresh", true)
}
ThemeConfig.preventBackgroundRefresh = true
}
fun onActivityResume() {
if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) {
loadCustomBackground()
}
}
private fun loadThemeMode() {
}
private fun loadThemeColors() {
}
private fun loadDynamicColorState() {
}
private fun loadCustomBackground() {
}
}

View File

@@ -1,4 +1,4 @@
package com.sukisu.ultra.flash
package zako.zako.zako.zakoui.flash
import android.app.Activity
import android.content.Context
@@ -27,6 +27,11 @@ import java.io.File
import java.io.FileOutputStream
import java.io.IOException
/**
* @author ShirkNeko
* @date 2025/5/31.
*/
data class FlashState(
val isFlashing: Boolean = false,
val isCompleted: Boolean = false,

View File

@@ -0,0 +1,418 @@
package zako.zako.zako.zakoui.screen
import android.net.Uri
import android.os.Environment
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Error
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.R
import zako.zako.zako.zakoui.flash.HorizonKernelState
import zako.zako.zako.zakoui.flash.HorizonKernelWorker
import com.sukisu.ultra.ui.component.KeyEventBlocker
import com.sukisu.ultra.ui.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import com.sukisu.ultra.ui.theme.CardConfig
import zako.zako.zako.zakoui.flash.FlashState
import kotlinx.coroutines.delay
/**
* @author ShirkNeko
* @date 2025/5/31.
*/
private object KernelFlashStateHolder {
var currentState: HorizonKernelState? = null
var currentUri: Uri? = null
var currentSlot: String? = null
var isFlashing = false
}
/**
* Kernel刷写界面
*/
@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@Composable
fun KernelFlashScreen(
navigator: DestinationsNavigator,
kernelUri: Uri,
selectedSlot: String? = null
) {
val context = LocalContext.current
val scrollState = rememberScrollState()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val snackBarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
var logText by rememberSaveable { mutableStateOf("") }
var showFloatAction by rememberSaveable { mutableStateOf(false) }
val logContent = rememberSaveable { StringBuilder() }
val horizonKernelState = remember {
if (KernelFlashStateHolder.currentState != null &&
KernelFlashStateHolder.currentUri == kernelUri &&
KernelFlashStateHolder.currentSlot == selectedSlot) {
KernelFlashStateHolder.currentState!!
} else {
HorizonKernelState().also {
KernelFlashStateHolder.currentState = it
KernelFlashStateHolder.currentUri = kernelUri
KernelFlashStateHolder.currentSlot = selectedSlot
KernelFlashStateHolder.isFlashing = false
}
}
}
val flashState by horizonKernelState.state.collectAsState()
val logSavedString = stringResource(R.string.log_saved)
val onFlashComplete = {
showFloatAction = true
KernelFlashStateHolder.isFlashing = false
}
// 开始刷写
LaunchedEffect(Unit) {
if (!KernelFlashStateHolder.isFlashing && !flashState.isCompleted && flashState.error.isEmpty()) {
withContext(Dispatchers.IO) {
KernelFlashStateHolder.isFlashing = true
val worker = HorizonKernelWorker(
context = context,
state = horizonKernelState,
slot = selectedSlot
)
worker.uri = kernelUri
worker.setOnFlashCompleteListener(onFlashComplete)
worker.start()
// 监听日志更新
while (!flashState.isCompleted && flashState.error.isEmpty()) {
if (flashState.logs.isNotEmpty()) {
logText = flashState.logs.joinToString("\n")
logContent.clear()
logContent.append(logText)
}
delay(100)
}
if (flashState.error.isNotEmpty()) {
logText += "\n${flashState.error}\n"
logContent.append("\n${flashState.error}\n")
KernelFlashStateHolder.isFlashing = false
} else if (flashState.isCompleted) {
logText += "\n${context.getString(R.string.horizon_flash_complete)}\n\n\n"
logContent.append("\n${context.getString(R.string.horizon_flash_complete)}\n\n\n")
}
}
} else {
logText = flashState.logs.joinToString("\n")
if (flashState.error.isNotEmpty()) {
logText += "\n${flashState.error}\n"
} else if (flashState.isCompleted) {
logText += "\n${context.getString(R.string.horizon_flash_complete)}\n\n\n"
showFloatAction = true
}
}
}
val onBack: () -> Unit = {
if (!flashState.isFlashing || flashState.isCompleted || flashState.error.isNotEmpty()) {
// 清理全局状态
if (flashState.isCompleted || flashState.error.isNotEmpty()) {
KernelFlashStateHolder.currentState = null
KernelFlashStateHolder.currentUri = null
KernelFlashStateHolder.currentSlot = null
KernelFlashStateHolder.isFlashing = false
}
navigator.popBackStack()
}
}
BackHandler(enabled = true) {
onBack()
}
Scaffold(
topBar = {
TopBar(
flashState = flashState,
onBack = onBack,
onSave = {
scope.launch {
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
val date = format.format(Date())
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"KernelSU_kernel_flash_log_${date}.log"
)
file.writeText(logContent.toString())
snackBarHost.showSnackbar(logSavedString.format(file.absolutePath))
}
},
scrollBehavior = scrollBehavior
)
},
floatingActionButton = {
if (showFloatAction) {
ExtendedFloatingActionButton(
onClick = {
scope.launch {
withContext(Dispatchers.IO) {
reboot()
}
}
},
icon = {
Icon(
Icons.Filled.Refresh,
contentDescription = stringResource(id = R.string.reboot)
)
},
text = {
Text(text = stringResource(id = R.string.reboot))
},
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
expanded = true
)
}
},
snackbarHost = { SnackbarHost(hostState = snackBarHost) },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
containerColor = MaterialTheme.colorScheme.background
) { innerPadding ->
KeyEventBlocker {
it.key == Key.VolumeDown || it.key == Key.VolumeUp
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.nestedScroll(scrollBehavior.nestedScrollConnection),
) {
FlashProgressIndicator(flashState)
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.verticalScroll(scrollState)
) {
LaunchedEffect(logText) {
scrollState.animateScrollTo(scrollState.maxValue)
}
Text(
modifier = Modifier.padding(16.dp),
text = logText,
style = MaterialTheme.typography.bodyMedium,
fontFamily = FontFamily.Monospace,
color = MaterialTheme.colorScheme.onSurface
)
}
}
}
}
@Composable
private fun FlashProgressIndicator(flashState: FlashState) {
val progressColor = when {
flashState.error.isNotEmpty() -> MaterialTheme.colorScheme.error
flashState.isCompleted -> MaterialTheme.colorScheme.tertiary
else -> MaterialTheme.colorScheme.primary
}
val progress = animateFloatAsState(
targetValue = flashState.progress,
label = "FlashProgress"
)
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = when {
flashState.error.isNotEmpty() -> stringResource(R.string.flash_failed)
flashState.isCompleted -> stringResource(R.string.flash_success)
else -> stringResource(R.string.flashing)
},
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
color = progressColor
)
when {
flashState.error.isNotEmpty() -> {
Icon(
imageVector = Icons.Default.Error,
contentDescription = null,
tint = MaterialTheme.colorScheme.error
)
}
flashState.isCompleted -> {
Icon(
imageVector = Icons.Default.CheckCircle,
contentDescription = null,
tint = MaterialTheme.colorScheme.tertiary
)
}
}
}
Spacer(modifier = Modifier.height(8.dp))
if (flashState.currentStep.isNotEmpty()) {
Text(
text = flashState.currentStep,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(8.dp))
}
LinearProgressIndicator(
progress = { progress.value },
modifier = Modifier
.fillMaxWidth()
.height(8.dp),
color = progressColor,
trackColor = MaterialTheme.colorScheme.surfaceVariant
)
if (flashState.error.isNotEmpty()) {
Spacer(modifier = Modifier.height(8.dp))
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Error,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(16.dp)
)
}
Spacer(modifier = Modifier.height(4.dp))
Text(
text = flashState.error,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error,
modifier = Modifier
.fillMaxWidth()
.background(
MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.3f),
shape = MaterialTheme.shapes.small
)
.padding(8.dp)
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(
flashState: FlashState,
onBack: () -> Unit,
onSave: () -> Unit = {},
scrollBehavior: TopAppBarScrollBehavior? = null
) {
val statusColor = when {
flashState.error.isNotEmpty() -> MaterialTheme.colorScheme.error
flashState.isCompleted -> MaterialTheme.colorScheme.tertiary
else -> MaterialTheme.colorScheme.primary
}
val colorScheme = MaterialTheme.colorScheme
val cardColor = if (CardConfig.isCustomBackgroundEnabled) {
colorScheme.surfaceContainerLow
} else {
colorScheme.background
}
val cardAlpha = CardConfig.cardAlpha
TopAppBar(
title = {
Text(
text = stringResource(
when {
flashState.error.isNotEmpty() -> R.string.flash_failed
flashState.isCompleted -> R.string.flash_success
else -> R.string.kernel_flashing
}
),
style = MaterialTheme.typography.titleLarge,
color = statusColor
)
},
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurface
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = cardColor.copy(alpha = cardAlpha),
scrolledContainerColor = cardColor.copy(alpha = cardAlpha)
),
actions = {
IconButton(onClick = onSave) {
Icon(
imageVector = Icons.Filled.Save,
contentDescription = stringResource(id = R.string.save_log),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
},
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
scrollBehavior = scrollBehavior
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,21 +2,772 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
android:viewportWidth="512"
android:viewportHeight="512">
<group android:scaleX="0.45"
android:scaleY="0.45"
android:translateX="140.8"
android:translateY="140.8">
<group
android:scaleX="0.135"
android:scaleY="0.135">
<path
android:pathData="M 259 259 H 541 V 541 H 259 V 259 Z"
android:strokeWidth="18"
android:strokeColor="#1e110d" />
<path
android:fillColor="#1e110d"
android:pathData="M 257 257 H 407 V 407 H 257 V 257 Z" />
<path
android:fillColor="#1e110d"
android:pathData="M 393 393 H 543 V 543 H 393 V 393 Z" />
</group>
<path
android:fillColor="#fff9f6"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M164.239,424.641 L133.168,450.694 C133.168,450.694,127.975,468.87,159.134,479.627 C190.293,490.384,372.425,481.111,372.425,481.111 C372.425,481.111,424.728,492.981,453.29,444.017 C452.919,444.759,444.387,463.306,444.387,463.306 L445.871,482.595 C445.871,482.595,503.606,473.181,476.327,333.64 C480.524,324.197,484.72,318.427,484.72,318.427 L481.572,296.657 C481.572,296.657,487.23,256.322,489.455,251.128 C491.681,245.935,499.099,240,499.099,240 C499.099,240,496.317,219.598,492.422,213.849 C491.68,210.511,499.284,178.795,494.462,155.797 C487.414,143.927,489.64,143.927,475.544,130.202 C475.544,130.573,484.447,85.3181,474.802,56.7556 C471.834,56.7556,442.53,45.9983,377.986,80.4958 C377.615,81.6086,351.649,50.4496,224.416,64.5453 C223.674,64.5453,181.016,26.7093,157.647,24.1127 C156.534,23.7418,135.02,20.0323,132.423,73.0769 C132.423,74.1897,88.281,89.0274,76.4109,137.992 C64.5408,186.956,65.2827,218.857,65.2827,218.857 C65.2827,218.857,46.3778,246.52,55.4846,243.89 C60.8314,242.346,60.4605,267.45,60.4605,267.45 L58.2349,306.77 C58.2349,306.77,43.7682,342.009,43.3972,366.492 C43.0263,390.974,42.6553,400.619,48.2194,415.085 C53.7835,429.552,68.9921,461.824,90.8776,478.145 C99.7802,478.516,109.796,480,109.796,480 L113.505,459.227 C113.505,459.227,132.423,475.177,145.777,474.065 C159.131,472.952,145.035,473.323,145.035,473.323 L131.31,455.518 Z" />
<path
android:fillColor="#93d4fa"
android:strokeColor="#4c4f59"
android:strokeWidth="6.9"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M133.91,110.17 L64.1717,157.65 L82.4357,188.404 L50.8182,244.079 L79.3807,261.142 L94.2186,259.658 L96.4452,204.017 L99.023,199.626 L117.217,177.681 L140.957,127.974 Z" />
<path
android:fillColor="#ace0fe"
android:strokeColor="#4c4f59"
android:strokeWidth="6.54357"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M217.014,405.828 C217.014,405.828,224.431,379.883,258.18,378.755 C291.929,377.627,311.955,382.891,311.955,382.891 C311.955,382.891,323.081,389.283,329.756,397.555 C336.432,405.827,358.683,438.164,371.293,446.436 C383.902,454.708,386.498,461.852,386.498,461.852 L384.644,473.132 C384.644,473.132,369.809,483.284,353.862,484.788 C337.915,486.292,206.258,484.788,206.258,484.788 L176.218,481.78" />
<path
android:fillColor="#ffffff"
android:strokeWidth="1.2"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M363.541,444.682 C363.541,444.682,356.647,448.748,358.68,451.488 C360.713,454.228,364.602,454.935,364.602,454.935 C364.602,454.935,369.198,455.2,369.729,451.399 C370.259,447.598,366.724,444.593,366.724,444.593" />
<path
android:fillColor="#ffffff"
android:strokeWidth="1.2"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M349.677,459.826 C349.677,459.826,354.177,452.576,356.677,454.326 C359.177,456.076,352.427,461.576,352.427,461.576" />
<path
android:fillColor="#ffffff"
android:strokeWidth="8.7"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M228.97,465.109 A8.0341015,5.6995392,0,0,1,220.936,470.809 A8.0341015,5.6995392,0,0,1,212.902,465.109 A8.0341015,5.6995392,0,0,1,220.936,459.409 A8.0341015,5.6995392,0,0,1,228.97,465.109 Z" />
<path
android:strokeColor="#6c9cb2"
android:strokeWidth="3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M220.852,408.918 C220.625,409.655,221.622,410.397,221.054,409.289 C220.495,408.199,221.513,410.375,221.635,410.685 C223.736,416.022,223.599,421.847,225.242,427.328 C225.544,428.335,225.83,429.367,225.836,430.427" />
<path
android:strokeColor="#6c9cb2"
android:strokeWidth="6.9"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M225.574,436.984 C224.352,441.357,225.651,446.212,227.992,449.836 C230.334,453.462,228.851,458.145,230.558,461.902 C232.274,465.678,232.769,470.295,230.257,474.062 C228.277,477.032,225.018,479.198,223.87,482.677 C223.87,482.677,223.796,483.338,223.796,483.338" />
<path
android:fillColor="#fbf3ef"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M259.148,381.902 C259.148,381.902,268.066,397.377,268.328,402.886 C268.59,408.394,300.59,401.05,300.59,401.05 C300.59,401.05,296.131,384.263,293.246,381.64 C290.361,379.017,259.148,381.902,259.148,381.902 Z" />
<path
android:strokeColor="#6c9cb2"
android:strokeWidth="4.6"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M247.869,386.361 C248.493,387.123,249.181,387.683,248.222,386.467 C247.237,385.218,247.587,385.904,248.316,386.709 C249.718,388.257,250.625,390.115,251.419,392.051 C252.907,395.677,253.575,399.582,255.44,403.076 C256.689,405.416,258.189,407.614,259.411,409.968" />
<path
android:strokeColor="#6c9cb2"
android:strokeWidth="4.6"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M306.623,386.885 C308.253,392.964,310.665,398.821,313.443,404.459" />
<path
android:strokeColor="#6c9cb2"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M335.738,431.213 C336.508,434.977,338.987,438.481,341.225,442.105 C343.323,445.501,344.561,449.237,346.104,452.901 C348.174,457.813,352.855,461.643,353.803,466.855 C354.723,471.917,359.045,475.752,359.082,481.05" />
<path
android:strokeColor="#494d55"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M258.623,384.262 C258.623,384.262,266.754,391.082,269.902,411.278" />
<path
android:fillColor="#fbf3ef"
android:strokeColor="#4c4f59"
android:strokeWidth="6.9"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M127.233,329.025 C127.233,329.025,124.636,389.488,140.958,404.326 C149.119,396.165,150.973,393.94,150.973,393.94 L163.214,402.472 L168.407,398.021 L170.633,378.361 L175.826,368.346 L181.761,356.105 L173.229,347.573 L157.279,328.284 L153.941,323.462 L149.49,332.736 C149.49,332.736,137.249,332.736,133.54,330.139 C129.831,327.542,127.234,329.026,127.234,329.026 Z" />
<path
android:strokeColor="#494d55"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M291.672,381.377 C291.672,381.377,298.754,387.934,301.902,407.869" />
<path
android:strokeColor="#494d55"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M270.164,404.197 C270.164,404.197,286.164,400.263,297.967,401.574" />
<path
android:strokeColor="#494d55"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M253.902,417.836 C253.902,417.836,291.148,404.721,323.672,408.656 C325.77,412.328,329.442,425.443,317.902,429.64 C313.181,429.64,298.754,428.066,295.345,429.115 C291.935,430.164,282.755,429.64,276.984,433.574 C271.214,437.508,262.295,442.492,258.623,436.722 C254.951,430.952,253.902,424.394,253.902,424.394 Z" />
<path
android:strokeColor="#494d55"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M276.459,482.623 C276.459,482.623,266.754,481.049,277.508,436.197" />
<path
android:fillColor="#fcf6fa"
android:strokeColor="#494d55"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M287.213,432.787 L293.508,483.672 L307.41,482.885 L300.59,428.852 Z" />
<path
android:strokeColor="#494d55"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M287.738,430.951 L293.508,481.836" />
<path
android:strokeColor="#494d55"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M300.328,428.066 C300.328,428.066,302.426,465.837,307.672,481.05" />
<path
android:strokeColor="#494d55"
android:strokeWidth="5.3"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M313.18,431.213 C313.18,431.213,320.787,458.754,323.41,467.147 C326.033,475.54,315.279,482.885,315.279,482.885" />
<path
android:fillColor="#bce4fd"
android:strokeWidth="8.7"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M164.239,424.641 L133.168,450.694 L138.176,464.419 L159.134,479.628 L176.219,481.782 L165.09,461.23 L163.544,438.446 Z" />
<path
android:fillColor="#ffffff"
android:strokeWidth="8.7"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M163.818,451.101 A8.0341015,5.6995392,0,0,1,155.784,456.801 A8.0341015,5.6995392,0,0,1,147.75,451.101 A8.0341015,5.6995392,0,0,1,155.784,445.401 A8.0341015,5.6995392,0,0,1,163.818,451.101 Z" />
<path
android:fillColor="#fff9f6"
android:strokeWidth="8.7"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M338.361,376.131 C338.361,376.131,340.197,407.082,332.066,427.541 C334.951,428.59,346.547,427.807,358.979,420.199 C371.411,412.591,370.361,398.164,370.361,398.164 L372.459,427.016 C372.459,427.016,396.778,398.917,396.684,374.409 C396.59,349.901,395.016,348.327,395.016,348.327 L383.182,347.2 Z" />
<path
android:fillColor="#fde9e7"
android:strokeWidth="2.845"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M339.782,389.859 C339.782,389.859,377.989,382.069,396.165,363.893 C401.729,372.425,404.326,396.536,404.326,396.536 L406.923,409.519 L423.244,346.83 L416.567,341.637 L410.632,324.203 C410.632,324.203,395.423,339.412,383.182,347.201 C370.941,354.991,349.055,366.49,349.055,366.49 L338.298,370.199 Z" />
<path
android:strokeColor="#c3b4b0"
android:strokeWidth="2.845"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M347.016,369.643 C347.016,369.643,349.242,379.658,345.161,389.488 C341.081,399.318,340.153,398.947,340.153,398.947" />
<path
android:fillColor="#fde9e7"
android:strokeWidth="6.945"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M380.852,480 C380.852,480,425.442,462.689,440.131,392.918 C446.951,390.295,474.229,378.229,479.475,367.213 C482.098,357.246,475.803,335.213,475.803,335.213 L451.147,335.738 L424.393,340.459 L419.672,352 L411.279,404.459 L398.164,432.787 L382.426,468.984 Z" />
<path
android:strokeColor="#4c4f59"
android:strokeWidth="8.74203"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M147.656,277.034 C147.656,277.034,136.529,381.17,275.24,380.421 C413.951,379.672,425.078,280.031,425.078,280.031" />
<path
android:fillColor="#fff9f6"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M123.279,147.934 L428.066,169.442 L403.41,129.049 C403.41,129.049,447.476,70.2949,454.295,68.7211 C461.115,67.1473,470.033,58.7539,470.033,58.7539 L456.918,55.0818 L379.803,77.1146 C379.803,77.1146,257.049,43.5408,211.41,70.8195 C165.771,98.0982,141.639,116.983,141.639,116.983 Z" />
<path
android:fillColor="#fde9e7"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M331.445,66.6518 L347.674,97.4945 L395.504,128.862 L401.81,129.94 L428.693,94.6046 L396.59,72.3936 L377.705,77.6395 L347.279,66.0985 Z" />
<path
android:fillColor="#ace0fe"
android:strokeWidth="14.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M431.034,175.826 L448.097,158.763 L498.545,230.726 L492.61,248.531 L459.225,267.078 L446.613,250.757 L464.418,239.629 Z" />
<path
android:fillColor="#ace0fe"
android:strokeColor="#4c4f59"
android:strokeWidth="6.945"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M465.902,261.885 C465.902,261.885,501.512,316.413,468.87,335.331 C443.646,339.411,420.648,337.557,420.648,337.557 L431.034,311.22 L452.549,269.304 Z" />
<path
android:fillColor="#febdc7"
android:strokeWidth="4.6"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M471.869,57.7049 C471.869,57.7049,488.918,126.689,460.853,168.131 C453.509,168.393,451.673,157.639,451.673,157.639 L453.771,151.082 L452.722,141.902 L455.083,131.148 L456.919,125.64 L450.624,119.607 L444.067,114.361 L441.182,109.115 L444.067,103.869 L441.444,98.6231 L437.247,95.7379 L434.099,94.1641 L439.345,82.6231 L451.148,75.2788 Z" />
<path
android:fillColor="#ff1c1c"
android:fillAlpha="0.0509804"
android:strokeWidth="3.92397"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M244.267,326.814 A35.587568,21.480417,0,0,1,208.679,348.294 A35.587568,21.480417,0,0,1,173.091,326.814 A35.587568,21.480417,0,0,1,208.679,305.334 A35.587568,21.480417,0,0,1,244.267,326.814 Z" />
<path
android:fillColor="#ff1c1c"
android:fillAlpha="0.05044398"
android:strokeWidth="3.65169"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M355.204,336.752 A31.611719,20.942554,0,0,1,323.592,357.695 A31.611719,20.942554,0,0,1,291.98,336.752 A31.611719,20.942554,0,0,1,323.592,315.809 A31.611719,20.942554,0,0,1,355.204,336.752 Z" />
<path
android:fillColor="#fbf3ef"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M188.328,343.607 C188.328,343.607,200.394,315.279,204.066,349.902 C209.312,340.984,214.033,332.591,221.377,341.509 C228.197,343.607,236.59,348.853,226.098,363.542 C215.606,378.231,242.436,345.171,241.543,361.909 C240.822,375.421,217.021,408.72,217.021,408.72 C217.021,408.72,210.683,472.905,183.606,478.951 C147.058,487.112,171.54,375.082,171.54,375.082 Z" />
<path
android:fillColor="#fbe7e5"
android:fillAlpha="0.40581462"
android:strokeWidth="10.4"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M193.631,383.924 A12.612,12.241061,0,0,1,181.019,396.165 A12.612,12.241061,0,0,1,168.407,383.924 A12.612,12.241061,0,0,1,181.019,371.683 A12.612,12.241061,0,0,1,193.631,383.924 Z" />
<path
android:fillColor="#fee4e0"
android:fillAlpha="0.42411327"
android:strokeWidth="10.4"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M179.097,356.526 L193.193,335.753 L200.241,337.237 L200.983,350.962 L212.482,339.463 L221.756,343.543 L230.288,349.478 L224.724,364.316 L238.449,359.494 L235.111,377.299 L187.26,349.849 Z" />
<path
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M188.328,343.607 C188.328,343.607,200.394,315.279,204.066,349.902 C209.312,340.984,214.033,332.591,221.377,341.509 C228.197,343.607,236.59,348.853,226.098,363.542 C215.606,378.231,242.436,345.171,241.543,361.909 C240.822,375.421,217.021,408.72,217.021,408.72 C217.021,408.72,210.683,472.905,183.606,478.951 C147.058,487.112,171.54,375.082,171.54,375.082 Z" />
<path
android:fillColor="#fff9f6"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M137.443,123.279 C137.443,123.279,186.782,74.6292,224.42,64.5441 C224.525,65.3118,104.253,-57.6139,137.443,123.279 Z" />
<path
android:fillColor="#fde9e7"
android:strokeWidth="1.18016"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M198.852,42 L135.218,81.0166 L139.059,121.956 L184.828,87 L224,64.5 Z" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0.465732"
android:strokeColor="#a18f90"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M404.964,131.773 C405.489,131.004,406,130.224,406.539,129.465 C409.363,125.492,412.359,121.645,415.217,117.696 C417.61,114.388,419.157,112.153,421.508,108.796 C426.834,101.216,432.688,94.0352,438.717,87.009 C441.739,83.4584,444.784,79.9101,448.194,76.7199 C450.156,74.8844,452.413,73.0471,454.468,71.3278 C459.576,66.9703,465.137,63.2182,470.69,59.4657 C470.69,59.4657,467.765,57.6015,467.765,57.6015 L467.765,57.6015 C462.349,61.5016,456.862,65.3066,451.773,69.6416 C449.774,71.287,447.329,73.2528,445.41,75.0108 C441.958,78.1737,438.921,81.7424,435.922,85.3274 C429.967,92.4153,424.289,99.7266,418.922,107.272 C413.451,115.001,407.959,122.727,401.809,129.941 Z" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0.465732"
android:strokeColor="#a18f90"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M431.705,96.8851 C432.388,96.8683,433.075,96.8931,433.759,96.9175 C435.025,96.9602,436.284,97.0933,437.535,97.2918 C438.884,97.5112,440.212,97.8397,441.533,98.1893 C442.837,98.5317,444.12,98.9495,445.392,99.3955 C445.74,99.5276,446.281,99.7279,446.624,99.8789 C446.813,99.9624,447.362,100.253,447.184,100.147 C442.264,97.2348,444.346,98.2776,445.168,99.2354 C445.635,100.178,444.914,101.046,444.335,101.751 C443.282,102.866,441.955,103.674,440.684,104.516 C439.979,105.042,438.841,105.512,438.657,106.495 C438.521,107.219,438.804,107.502,439.115,108.14 C439.373,108.455,439.611,108.787,439.889,109.085 C440.52,109.761,441.438,110.538,442.141,111.121 C443.425,112.185,444.754,113.196,446.067,114.223 C448.295,115.955,450.534,117.67,452.508,119.693 C453.753,121.058,454.844,122.554,455.616,124.235 C456.19,125.547,456.235,126.917,456.081,128.315 C455.929,129.857,455.069,131.063,454.111,132.22 C452.86,133.613,451.408,134.8,450.128,136.163 C449.51,136.928,448.934,137.773,448.792,138.77 C448.716,139.301,448.777,139.643,448.823,140.171 C449.005,141.517,449.421,142.813,449.857,144.095 C450.319,145.456,450.958,146.746,451.568,148.044 C452.147,149.17,452.449,150.372,452.547,151.627 C452.576,152.64,452.406,153.643,452.197,154.63 C452.014,155.489,451.806,156.25,451.273,156.96 C450.579,157.686,449.748,158.26,448.962,158.879 C448.962,158.879,452.094,160.848,452.094,160.848 L452.094,160.848 C452.901,160.205,453.758,159.61,454.468,158.856 C455.052,158.078,455.277,157.331,455.473,156.387 C455.691,155.367,455.861,154.331,455.857,153.286 C455.781,151.991,455.511,150.736,454.918,149.57 C454.311,148.282,453.67,147.005,453.196,145.659 C452.758,144.401,452.34,143.13,452.125,141.811 C452.072,141.379,451.995,140.958,452.039,140.519 C452.134,139.578,452.689,138.785,453.257,138.067 C454.534,136.692,455.986,135.496,457.243,134.102 C458.25,132.898,459.168,131.637,459.347,130.028 C459.522,128.576,459.514,127.143,458.951,125.764 C458.207,124.025,457.101,122.494,455.846,121.083 C455.078,120.279,454.934,120.098,454.085,119.342 C452.574,117.998,450.931,116.813,449.361,115.541 C448.052,114.517,446.728,113.509,445.442,112.456 C444.732,111.875,443.869,111.153,443.218,110.494 C442.948,110.22,442.706,109.919,442.45,109.632 C442.2,109.235,441.825,108.834,441.875,108.312 C441.954,107.485,443.212,106.929,443.774,106.494 C445.073,105.627,446.429,104.795,447.505,103.65 C448.192,102.813,448.909,101.854,448.58,100.714 C448.498,100.578,448.435,100.429,448.335,100.306 C448.225,100.172,448.104,100.04,447.956,99.9498 C446.131,98.8402,444.47,97.783,442.482,97.127 C441.204,96.6881,439.915,96.2798,438.606,95.9437 C437.278,95.6019,435.942,95.2832,434.585,95.0766 C433.319,94.8915,432.044,94.7705,430.765,94.7225 C430.077,94.6977,429.373,94.7137,428.694,94.6018 Z" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0.465732"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M472.565,132.892 C472.58,133.665,472.464,134.438,472.369,135.204 C472.142,136.869,471.719,138.496,471.276,140.113 C470.664,142.278,469.96,144.416,469.202,146.534 C468.343,148.936,467.316,151.271,466.158,153.543 C465.033,155.731,463.751,157.831,462.385,159.876 C461.185,161.66,459.873,163.363,458.525,165.037 C457.749,165.984,456.962,166.921,456.176,167.859 C456.176,167.859,459.27,169.717,459.27,169.717 L459.27,169.717 C460.051,168.773,460.833,167.83,461.601,166.875 C462.941,165.183,464.252,163.468,465.446,161.67 C466.811,159.608,468.088,157.491,469.22,155.29 C470.384,153.005,471.417,150.659,472.298,148.249 C473.071,146.13,473.791,143.992,474.423,141.827 C474.89,140.207,475.334,138.577,475.608,136.911 C475.723,136.144,475.823,135.375,475.929,134.607 Z" />
<path
android:fillColor="#fff9f6"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M322.623,86.0328 C322.623,86.0328,398.056,112.153,432.787,175.213 C466.23,235.934,465.836,237.639,465.836,237.639 C465.836,237.639,466.361,246.032,445.902,249.18 C443.279,249.18,460.591,280.131,465.836,286.426 C451.147,293.77,449.049,295.344,449.049,295.344 C449.049,295.344,455.344,303.213,453.77,311.082 C436.459,310.557,431.213,310.033,431.213,310.033 C431.213,310.033,427.016,327.344,415.475,343.082 C410.23,333.115,410.23,320,410.23,320 C410.23,320,386.098,356.721,337.836,361.443 C336.262,358.82,350.426,347.279,355.672,333.115 C344.131,334.689,340.983,335.213,340.983,335.213 L351.475,318.426 C351.475,318.426,362.491,294.295,360.918,290.098 C359.869,287.475,328.918,232.393,328.918,232.393 C328.918,232.393,304.262,265.442,285.377,282.229 C278.557,278.032,271.738,267.54,271.738,267.54 L259.148,279.081 C259.148,279.081,210.71,228.317,217.054,188.898 C213.906,200.964,217.181,245.507,211.411,251.278 C206.165,249.18,195.149,236.589,197.247,210.36 C196.722,209.835,167.345,251.803,167.345,251.803 C167.345,251.803,163.148,300.59,183.607,325.246 C185.181,327.869,182.033,341.508,152.132,322.623 C153.181,321.049,151.083,332.59,151.083,332.59 C151.083,332.59,104.394,334.688,105.444,271.738 C102.821,269.115,91.8047,305.836,91.8047,305.836 C91.8047,305.836,67.6735,273.836,91.2801,212.984 C103.083,182.558,114.624,160.132,123.215,145.312 C131.805,130.492,137.445,123.279,137.445,123.279" />
<path
android:fillColor="#fffdfc"
android:strokeWidth="7.4278"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M355.672,146.885 A91.803276,41.967213,0,0,1,263.869,188.852 A91.803276,41.967213,0,0,1,172.066,146.885 A91.803276,41.967213,0,0,1,263.869,104.918 A91.803276,41.967213,0,0,1,355.672,146.885 Z" />
<path
android:fillColor="#fde9e7"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M349.377,329.443 C349.377,329.443,420.721,287.476,428.066,238.164 C428.066,239.213,454.296,266.492,454.296,266.492 L465.837,290.623 L450.099,297.967 L452.197,311.606 L430.164,310.557 L417.574,340.983 L404.984,323.147 L381.902,349.377 L336.787,364.066 Z" />
<path
android:fillColor="#fde9e7"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M325.246,144.787 C325.246,144.787,335.738,188.853,327.344,223.476 C327.344,233.968,360.918,284.328,360.918,284.328 C360.918,284.328,375.607,212.984,335.738,152.131 C327.345,146.885,325.246,144.787,325.246,144.787 Z" />
<path
android:fillColor="#fde9e7"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M186.754,166.82 L209.836,167.869 C209.836,167.869,195.147,194.099,196.197,214.033 C194.099,210.885,174.164,245.508,174.164,245.508 C174.164,245.508,164.721,230.819,171.016,195.147 C180.459,178.36,186.754,166.819,186.754,166.819 Z" />
<path
android:fillColor="#fffdfc"
android:strokeWidth="6.94619"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M340.28,112.237 A80.289719,41.967213,0,0,1,259.99,154.204 A80.289719,41.967213,0,0,1,179.7,112.237 A80.289719,41.967213,0,0,1,259.99,70.2698 A80.289719,41.967213,0,0,1,340.28,112.237 Z" />
<path
android:strokeColor="#a18f90"
android:strokeWidth="7.7"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M138.623,122.492 C138.623,122.492,192.262,69.5084,231.607,64.2625" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#c3b4b0"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M436.459,268.066 C436.459,268.066,440.131,286.427,433.836,310.558" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#c3b4b0"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M418.098,305.836 L409.18,323.672" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#c3b4b0"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M377.705,300.59 C377.705,300.59,380.328,312.656,355.672,332.59" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#c3b4b0"
android:strokeWidth="4.2"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M284.328,97.5738 C284.328,97.5738,296.394,136.918,256.525,216.656 C257.05,216.656,310.033,177.836,312.656,127.476" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#c3b4b0"
android:strokeWidth="5.1"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M272.787,87.082 C272.787,87.082,286.426,78.6886,291.672,86.5574 C308.845,97.3968,304.705,116.794,315.869,130.951 C317.662,132.804,323.041,130.17,323.672,132.721 C328.553,152.446,338.488,183.527,327.996,232.839" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#c3b4b0"
android:strokeWidth="4.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M171.004,241.854 C171.004,241.854,142.442,181.762,263.368,94.219 C265.594,94.9609,204.017,148.377,197.34,205.502" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#c3b4b0"
android:strokeWidth="3"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M327.344,143.738 C327.344,143.738,352.524,164.197,359.344,209.312" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M362.492,268.59 L359.344,288.524" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M421.77,344.131 C421.77,344.131,419.147,402.361,388.196,455.869 C357.245,509.377,426.491,471.607,426.491,471.607" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M395.016,348.328 L407.082,408.656" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M371.934,423.869 C371.934,423.869,402.885,398.164,396.59,349.902" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M332.066,427.541 C332.066,427.541,368.787,429.115,370.886,398.164" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M372.459,427.016 L370.361,398.164" />
<path
android:fillColor="#93cefc"
android:strokeColor="#4d4e59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M377.509,228.61 C377.509,228.61,401.64,230.184,401.64,239.102 C401.115,242.774,390.099,256.938,378.033,257.987 C375.935,256.413,377.508,228.61,377.508,228.61 Z" />
<path
android:fillColor="#fffdfe"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M350.951,235.541 C350.951,235.541,344.394,213.508,352,213.508 C359.607,213.508,365.377,227.41,365.377,227.41 C365.377,227.41,370.436,209.683,375.344,212.197 C387.142,218.241,378.754,233.967,378.754,233.967 C378.754,233.967,397.377,259.934,366.688,261.246 C336.521,262.535,350.95,235.541,350.95,235.541 Z" />
<path
android:fillColor="#1a1a1a"
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M332.066,425.967 C332.066,425.967,342.558,400.787,338.361,376.131" />
<path
android:fillColor="#e85240"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M192.262,268.852 C192.262,268.852,176.787,290.885,189.639,304.262 C232.393,304.524,232.918,304.787,232.918,304.787 C232.918,304.787,244.197,296.394,238.426,272.787 C212.983,262.82,192.262,268.853,192.262,268.853 Z" />
<path
android:fillColor="#fbb579"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M188.066,301.115 C187.624,292.333,201.519,287.099,210.361,286.689 C219.033,286.287,233.601,290.074,233.443,298.755 C233.366,303.017,221.902,304.263,221.902,304.263 C221.902,304.263,188.635,312.429,188.066,301.115 Z" />
<path
android:fillColor="#ffffff"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M217.967,273.049 A7.8688526,7.0819674,0,0,1,210.098,280.131 A7.8688526,7.0819674,0,0,1,202.229,273.049 A7.8688526,7.0819674,0,0,1,210.098,265.967 A7.8688526,7.0819674,0,0,1,217.967,273.049 Z" />
<path
android:strokeColor="#ebc2bf"
android:strokeWidth="2.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M174.164,245.508 C189.25,240.742,205.703,238.055,221.258,241.993 C224.556,242.828,227.776,243.986,230.82,245.508" />
<path
android:fillColor="#fde9e7"
android:strokeWidth="6.945"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M477.772,132.797 C477.772,132.797,491.126,142.812,492.61,150.231 C494.094,157.65,494.094,207.727,494.094,207.727 L489.643,208.84 L458.855,171.375 C458.855,171.375,467.758,156.908,469.983,148.748 C472.209,140.587,477.773,132.798,477.773,132.798 Z" />
<path
android:fillColor="#e85240"
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M192.262,268.852 C192.262,268.852,176.787,290.885,189.639,304.262 C232.393,304.524,232.918,304.787,232.918,304.787 C232.918,304.787,244.197,296.394,238.426,272.787 C212.983,262.82,192.262,268.853,192.262,268.853 Z" />
<path
android:fillColor="#e85240"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M325.521,272.819 C342.546,273.885,364.341,295.901,343.357,321.081 C341.783,321.606,294.372,315.775,293.783,311.901 C292.977,306.601,285.826,270.333,325.521,272.819 Z" />
<path
android:fillColor="#fbb579"
android:strokeWidth="7.14054"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M293.046,306.321 C295.599,297.786,313.477,296.791,323.835,298.955 C333.994,301.077,349.503,308.913,346.293,317.177 C344.717,321.235,331.038,319.114,331.038,319.114 C331.038,319.114,289.756,317.315,293.045,306.321 Z" />
<path
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M325.521,272.819 C342.546,273.885,364.341,295.901,343.357,321.081 C341.783,321.606,294.372,315.775,293.783,311.901 C292.977,306.601,285.826,270.333,325.521,272.819 Z" />
<path
android:strokeColor="#ebc2bf"
android:strokeWidth="2.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M309.328,258.435 C309.328,258.435,335.558,253.189,346.049,262.632" />
<path
android:fillColor="#ffffff"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M329.634,284.993 A7.8688526,7.0819674,0,0,1,321.765,292.075 A7.8688526,7.0819674,0,0,1,313.896,284.993 A7.8688526,7.0819674,0,0,1,321.765,277.911 A7.8688526,7.0819674,0,0,1,329.634,284.993 Z" />
<path
android:strokeColor="#4c4f59"
android:strokeWidth="14.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M170.492,266.492 C170.492,266.492,202.492,246.558,240.787,264.394" />
<path
android:strokeColor="#4c4f59"
android:strokeWidth="14.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M292.997,274.177 C292.997,274.177,328.548,261.627,362.085,287.315" />
<path
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M431.034,176.197 L449.21,158.763 L490.755,212.92" />
<path
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M454.032,267.449 L497.247,243.523" />
<path
android:fillColor="#fff9f6"
android:fillAlpha="0"
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M164.239,424.641 L133.168,450.694 C133.168,450.694,127.975,468.87,159.134,479.627 C190.293,490.384,372.425,481.111,372.425,481.111 C372.425,481.111,424.728,492.981,453.29,444.017 C452.919,444.759,444.387,463.306,444.387,463.306 L445.871,482.595 C445.871,482.595,503.606,473.181,476.327,333.64 C480.524,324.197,484.72,318.427,484.72,318.427 L481.572,296.657 C481.572,296.657,487.23,256.322,489.455,251.128 C491.681,245.935,497.971,244.777,499.099,240 C501.168,231.244,496.317,219.598,492.422,213.849 C491.68,210.511,499.284,178.795,494.462,155.797 C487.414,143.927,489.64,143.927,475.544,130.202 C475.544,130.573,484.447,85.3181,474.802,56.7556 C471.834,56.7556,442.53,45.9983,377.986,80.4958 C377.615,81.6086,351.649,50.4496,224.416,64.5453 C223.674,64.5453,181.016,26.7093,157.647,24.1127 C156.534,23.7418,135.02,20.0323,132.423,73.0769 C132.423,74.1897,88.281,89.0274,76.4109,137.992 C64.5408,186.956,65.2827,218.857,65.2827,218.857 C65.2827,218.857,46.3778,246.52,55.4846,243.89 C60.8314,242.346,60.4605,267.45,60.4605,267.45 L58.2349,306.77 C58.2349,306.77,43.7682,342.009,43.3972,366.492 C43.0263,390.974,42.6553,400.619,48.2194,415.085 C53.7835,429.552,68.9921,461.824,90.8776,478.145 C99.7802,478.516,109.796,480,109.796,480 L113.505,459.227 C113.505,459.227,132.423,475.177,145.777,474.065 C159.131,472.952,145.035,473.323,145.035,473.323 L131.31,455.518 Z" />
<path
android:strokeColor="#c3b4b0"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M197.508,220.066 L215.869,207.738" />
<path
android:strokeColor="#c3b4b0"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M327.541,234.249 C327.541,234.249,321.269,239.154,311.683,233.258" />
<path
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M322.623,86.0328 C322.623,86.0328,398.056,112.153,432.787,175.213 C466.23,235.934,465.836,237.639,465.836,237.639 C465.836,237.639,466.361,246.032,445.902,249.18 C443.279,249.18,460.591,280.131,465.836,286.426 C451.147,293.77,449.049,295.344,449.049,295.344 C449.049,295.344,455.344,303.213,453.77,311.082 C436.459,310.557,431.213,310.033,431.213,310.033 C431.213,310.033,427.016,327.344,415.475,343.082 C410.23,333.115,410.23,320,410.23,320 C410.23,320,386.098,356.721,337.836,361.443 C336.262,358.82,350.426,347.279,355.672,333.115 C344.131,334.689,340.983,335.213,340.983,335.213 L351.475,318.426 C351.475,318.426,362.491,294.295,360.918,290.098 C359.869,287.475,328.918,232.393,328.918,232.393 C328.918,232.393,304.262,265.442,285.377,282.229 C278.557,278.032,271.738,267.54,271.738,267.54 L259.148,279.081 C259.148,279.081,210.71,228.317,217.054,188.898 C213.906,200.964,217.181,245.507,211.411,251.278 C206.165,249.18,195.149,236.589,197.247,210.36 C196.722,209.835,167.345,251.803,167.345,251.803 C167.345,251.803,163.148,300.59,183.607,325.246 C185.181,327.869,182.033,341.508,152.132,322.623 C153.181,321.049,151.083,332.59,151.083,332.59 C151.083,332.59,104.394,334.688,105.444,271.738 C102.821,269.115,91.8047,305.836,91.8047,305.836 C91.8047,305.836,67.6735,273.836,91.2801,212.984 C103.083,182.558,114.624,160.132,123.215,145.312 C131.805,130.492,137.445,123.279,137.445,123.279" />
<path
android:fillColor="#a88a8f"
android:strokeColor="#4c4f59"
android:strokeWidth="2.8623"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M216.292,255.539 A8.8904896,7.4928694,0,0,1,207.402,263.032 A8.8904896,7.4928694,0,0,1,198.512,255.539 A8.8904896,7.4928694,0,0,1,207.402,248.046 A8.8904896,7.4928694,0,0,1,216.292,255.539 Z" />
<path
android:fillColor="#a88a8f"
android:strokeColor="#4c4f59"
android:strokeWidth="2.8623"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M336.169,270.547 A8.8904896,7.4928694,0,0,1,327.279,278.04 A8.8904896,7.4928694,0,0,1,318.389,270.547 A8.8904896,7.4928694,0,0,1,327.279,263.054 A8.8904896,7.4928694,0,0,1,336.169,270.547 Z" />
<path
android:fillAlpha="0"
android:strokeColor="#a18f90"
android:strokeWidth="10.4"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M318.719,85.4618 C318.719,85.4618,369.167,102.525,395.504,128.862 C421.841,155.199,428.147,168.182,428.147,168.182" />
<path
android:strokeColor="#4c4f59"
android:strokeWidth="6.5"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M186.754,224.525 L197.246,225.574" />
<path
android:fillColor="#fcbeb5"
android:strokeColor="#e99c9b"
android:strokeWidth="3.4"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M250.942,349.983 C250.942,349.983,237.032,341.08,240.927,333.291 C244.822,325.501,258.732,328.84,258.732,328.84 C258.732,328.84,271.715,327.542,278.763,324.945 C285.811,322.348,288.407,329.953,288.407,329.953 C288.407,329.953,293.172,336.018,284.141,346.46 C278.206,353.322,262.997,353.508,262.997,353.508" />
<path
android:fillColor="#ffffff"
android:strokeColor="#e99c9b"
android:strokeWidth="1.8"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M276.209,325.765 C276.209,325.765,280.559,328.784,281.584,333.717 C282.155,333.823,284.918,330.63,284.56,326.844 C283.88,325.359,276.209,325.764,276.209,325.764 Z" />
<path
android:fillColor="#ffffff"
android:strokeColor="#e99c9b"
android:strokeWidth="1.8"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M245.007,329.396 L240.834,332.178 L240.741,338.206 L240.645,338.171 L247.718,329.354 Z" />
<path
android:strokeColor="#e99c9b"
android:strokeWidth="3.4"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M250.942,349.983 C250.942,349.983,237.032,341.08,240.927,333.291 C244.822,325.501,258.732,328.84,258.732,328.84 C258.732,328.84,271.715,327.542,278.763,324.945 C285.811,322.348,288.407,329.953,288.407,329.953 C288.407,329.953,293.172,336.018,284.141,346.46 C278.206,353.322,262.997,353.508,262.997,353.508" />
<path
android:fillColor="#ffffff"
android:strokeColor="#e89493"
android:strokeWidth="2.8"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M190.849,314.929 L196.97,329.952" />
<path
android:fillColor="#ffffff"
android:strokeColor="#e89493"
android:strokeWidth="2.8"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M204.945,316.969 L210.509,331.25" />
<path
android:fillColor="#ffffff"
android:strokeColor="#e89493"
android:strokeWidth="2.8"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M217.928,317.34 L225.347,333.847" />
<path
android:fillColor="#ffffff"
android:strokeColor="#e89493"
android:strokeWidth="2.8"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M300.648,328.468 L307.51,346.644" />
<path
android:fillColor="#ffffff"
android:strokeColor="#e89493"
android:strokeWidth="2.8"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M312.703,329.21 L319.38,346.273" />
<path
android:fillColor="#ffffff"
android:strokeColor="#e89493"
android:strokeWidth="2.8"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M326.243,328.654 L333.291,346.645" />
<path
android:strokeColor="#e49589"
android:strokeWidth="4.6"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M218.492,348.984 C216.619,350.289,215.156,352.098,213.789,353.9 C212.764,355.251,211.787,356.65,211.017,358.164" />
<path
android:strokeColor="#4c4f59"
android:strokeWidth="3.2"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M127.475,375.082 C127.475,375.082,113.049,355.934,115.409,315.541" />
<path
android:fillColor="#ffffff"
android:fillAlpha="0.734285"
android:strokeColor="#c1545a"
android:strokeWidth="7.8"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M136.393,322.098 C136.393,322.098,145.836,303.213,132.196,282.229 C129.048,281.704,159.999,254.426,137.442,205.114 C135.868,205.639,161.049,149.507,113.311,122.229 C109.114,113.836,67.6717,52.9831,24.1307,135.344 C24.1307,146.885,23.0815,171.541,23.0815,171.541 C23.0815,171.541,8.393,210.361,16.7864,243.41 C18.8848,246.558,-4.1972,293.246,27.8028,349.902 C28.3274,350.427,11.0159,407.607,40.393,432.787 C41.4422,437.508,54.0324,466.361,82.8848,472.131 C95.475,472.656,102.295,471.606,102.295,471.606 C102.295,471.606,133.77,466.885,150.033,404.458 C147.935,376.655,145.836,354.097,139.017,333.638" />
<path
android:fillColor="#ffffff"
android:fillAlpha="0.734285"
android:strokeColor="#000000"
android:strokeWidth="9"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M109.698,219.489 C109.698,219.489,94.4894,207.619,88.5543,199.829 M51.4602,219.86 L73.7167,197.974 M74.0876,183.878 L80.0227,225.794 M44.7833,201.683 L100.795,196.861 M111.923,179.427 L92.634,179.798 M92.2631,179.056 L90.7793,149.381 M51.8305,152.719 L89.6665,149.751 M64.8134,138.252 L65.5553,177.943" />
<path
android:fillColor="#ffffff"
android:fillAlpha="0.734285"
android:strokeColor="#000000"
android:strokeWidth="9"
android:strokeLineJoin="round"
android:strokeMiterLimit="1.3"
android:strokeLineCap="round"
android:pathData="M58.5078,352.286 L112.665,348.948 M66.2973,335.223 L101.908,332.255 M80.3934,299.983 L82.99,333.368 M64.072,317.047 L101.166,313.709 M102.65,332.256 L103.392,298.5 M103.021,298.871 L62.2175,301.097 M62.2175,300.726 L66.6688,334.111 M94.1184,285.889 L97.4569,271.422 M97.4569,269.938 L70.0072,271.422 M68.5234,286.26 L69.2653,266.229" />
<path
android:fillColor="#d96477"
android:strokeWidth="0.00475452"
android:pathData="M69.454,376.47 C65.2333,376.691,61.3746,378.322,58.6708,381.019 C55.6903,384.001,54.4288,388.12,55.1367,392.567 C56.0415,398.241,59.9003,404.306,66.5213,410.469 C67.9264,411.773,68.9164,412.618,70.9123,414.211 C74.4198,417.006,77.5919,419.223,81.9616,421.92 C83.5211,422.888,86.5974,424.685,87.7577,425.309 L88.093,425.492 L88.5827,425.224 C90.3657,424.243,93.5432,422.361,95.4273,421.172 C102.554,416.674,108.26,412.159,112.491,407.666 C119.352,400.378,122.268,393.281,120.922,387.173 C119.991,382.968,116.877,379.49,112.459,377.723 C110.559,376.963,108.712,376.568,106.62,376.474 C103.911,376.351,101.244,376.814,98.7003,377.846 C94.3679,379.6,90.7486,383.019,88.4706,387.513 C88.3109,387.827,88.1619,388.103,88.1406,388.12 C88.0714,388.175,87.9969,388.069,87.7095,387.491 C87.0282,386.132,85.6923,384.157,84.6438,382.968 C83.5687,381.741,81.8389,380.245,80.5509,379.426 C77.2244,377.307,73.3284,376.27,69.4537,376.47 Z" />
</group>
</vector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="0.135"
android:scaleY="0.135">
<path
android:pathData="M 259 259 H 541 V 541 H 259 V 259 Z"
android:strokeWidth="18"
android:strokeColor="#1e110d" />
<path
android:fillColor="#1e110d"
android:pathData="M 257 257 H 407 V 407 H 257 V 257 Z" />
<path
android:fillColor="#1e110d"
android:pathData="M 393 393 H 543 V 543 H 393 V 393 Z" />
</group>
</vector>

View File

@@ -1,22 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="0.135"
android:scaleY="0.135">
<path
android:pathData="M 259 259 H 541 V 541 H 259 V 259 Z"
android:strokeWidth="18"
android:strokeColor="#000000" />
<path
android:fillColor="#000000"
android:pathData="M 257 257 H 407 V 407 H 257 V 257 Z" />
<path
android:fillColor="#000000"
android:pathData="M 393 393 H 543 V 543 H 393 V 393 Z" />
</group>
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="m212.06,292.39c0,0 -14.77,-11.53 -20.53,-19.09m-36.03,19.45 l21.62,-21.26m0.36,-13.69 l5.76,40.71m-34.22,-23.42 l54.4,-4.68m10.81,-16.93 l-18.73,0.36m-0.36,-0.72 l-1.44,-28.82m-37.83,3.24 l36.75,-2.88m-24.14,-11.17 l0.72,38.55"
android:strokeLineJoin="round"
android:strokeWidth="8.74077"
android:fillColor="#ffffff"
android:strokeColor="#000000"
android:fillAlpha="0.734285"
android:strokeLineCap="round"/>
<path
android:pathData="m239.47,294.6 l48.58,-2.99m-41.59,-12.31 l31.94,-2.66m-19.3,-28.95 l2.33,29.94m-16.97,-14.64 l33.27,-2.99m1.33,16.64 l0.67,-30.28m-0.33,0.33 l-36.6,2m0,-0.33 l3.99,29.94m24.62,-43.25 l2.99,-12.98m0,-1.33 l-24.62,1.33m-1.33,13.31 l0.67,-17.97"
android:strokeLineJoin="round"
android:strokeWidth="8.07261"
android:fillColor="#ffffff"
android:strokeColor="#000000"
android:fillAlpha="0.734285"
android:strokeLineCap="round"/>
<path
android:pathData="m319.56,229.67c-3.9,0.24 -7.46,1.99 -9.95,4.89 -2.75,3.2 -3.91,7.63 -3.26,12.41 0.84,6.1 4.4,12.62 10.51,19.24 1.3,1.4 2.21,2.31 4.05,4.02 3.24,3 6.16,5.39 10.2,8.29 1.44,1.04 4.28,2.97 5.35,3.64l0.31,0.2 0.45,-0.29c1.65,-1.05 4.58,-3.08 6.32,-4.36 6.58,-4.83 11.84,-9.69 15.75,-14.52 6.33,-7.83 9.02,-15.46 7.78,-22.03 -0.86,-4.52 -3.73,-8.26 -7.81,-10.16 -1.75,-0.82 -3.46,-1.24 -5.39,-1.34 -2.5,-0.13 -4.96,0.37 -7.31,1.47 -4,1.89 -7.34,5.56 -9.44,10.39 -0.15,0.34 -0.28,0.63 -0.3,0.65 -0.06,0.06 -0.13,-0.05 -0.4,-0.68 -0.63,-1.46 -1.86,-3.58 -2.83,-4.86 -0.99,-1.32 -2.59,-2.93 -3.78,-3.81 -3.07,-2.28 -6.67,-3.39 -10.24,-3.18z"
android:strokeWidth="0.00473542"
android:fillColor="#000000"
android:strokeColor="#00000000"/>
</vector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="0.135"
android:scaleY="0.135">
<path
android:pathData="M 259 259 H 541 V 541 H 259 V 259 Z"
android:strokeWidth="18"
android:strokeColor="#000000" />
<path
android:fillColor="#000000"
android:pathData="M 257 257 H 407 V 407 H 257 V 257 Z" />
<path
android:fillColor="#000000"
android:pathData="M 393 393 H 543 V 543 H 393 V 393 Z" />
</group>
</vector>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
</adaptive-icon>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground_alt"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome_alt" />
</adaptive-icon>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground_alt"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome_alt" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -11,7 +11,7 @@
<string name="home_unsupported_reason">KernelSU يدعم GKI kernels فقط</string>
<string name="home_kernel">إصدار النواة</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">SuSFS Version</string>
<string name="home_susfs_version">إصدار SuSFS</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_manager_version">إصدار المدير</string>
<string name="home_fingerprint">البصمة</string>
@@ -28,7 +28,7 @@
<string name="module_sort_action_first">فرز (الإجراء أولاً)</string>
<string name="module_sort_enabled_first">فرز (الممكن أولاً)</string>
<string name="uninstall">إلغاء التثبيت</string>
<string name="restore">Restore</string>
<string name="restore">إسترجاع</string>
<string name="module_install">تثبيت الوحدة</string>
<string name="install">تثبيت</string>
<string name="reboot">إعادة تشغيل</string>
@@ -56,7 +56,7 @@
<string name="home_click_to_learn_kernelsu">تعرف على كيفية تثبيت KernelSU واستخدام الإضافات</string>
<string name="home_support_title">إدعمنا</string>
<string name="home_support_content">KernelSU سيظل دائماً مجانياً ومفتوح المصدر. مع ذلك، يمكنك أن تظهر لنا أنك تهتم بالتبرع.</string>
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
<string name="about_source_code"><![CDATA[أنظر إلى مصدر البرمجة في %1$s<br/>إنضم إلى قناتنا في %2$s ]]></string>
<string name="profile_default">الإفتراضي</string>
<string name="profile_template">نموذج</string>
<string name="profile_custom">مُخصّص</string>
@@ -70,10 +70,10 @@
<string name="profile_selinux_context">سياق SELinux</string>
<string name="profile_umount_modules">الغاء تحميل الإضافات</string>
<string name="failed_to_update_app_profile">فشل تحديث ملف تعريف التطبيق لـ %s</string>
<string name="require_kernel_version" formatted="false">The current KernelSU version %d is too low for the manager to work properly. Please upgrade to version %d or higher!</string>
<string name="require_kernel_version" formatted="false">إصدار KernelSU الحالي %d منخفض جدًا بحيث لا يعمل المدير بشكل صحيح. الرجاء الترقية إلى الإصدار %d أو أعلى!</string>
<string name="settings_umount_modules_default">الغاء تحميل الإضافات بشكل افتراضي</string>
<string name="settings_umount_modules_default_summary">القيمة الافتراضية العامة لـ\"إلغاء تحميل الإضافات\" في ملفات تعريف التطبيقات. إذا تم تمكينه، إزالة جميع تعديلات الإضافات على النظام للتطبيقات التي لا تحتوي على مجموعة ملف تعريف.</string>
<string name="settings_susfs_toggle">Disable kprobe hooks</string>
<string name="settings_susfs_toggle">تعطيل روابط kprobe</string>
<string name="profile_umount_modules_summary">سيسمح تمكين هذا الخيار لـKernelSU باستعادة أي ملفات معدلة بواسطة الإضافات لهذا التطبيق.</string>
<string name="profile_selinux_domain">المجال</string>
<string name="profile_selinux_rules">القواعد</string>
@@ -113,6 +113,7 @@
<string name="grant_root_failed">فشل في منح صلاحية الجذر!</string>
<string name="action">إجراء</string>
<string name="open">فتح</string>
<string name="close">إغلاق</string>
<string name="enable_web_debugging">تمكين تصحيح أخطاء WebView</string>
<string name="enable_web_debugging_summary">يمكن استخدامه لتصحيح أخطاء WebUI، يرجى تمكينه فقط عند الحاجة.</string>
<string name="direct_install">تثبيت مباشر (موصى به)</string>
@@ -137,234 +138,238 @@
<string name="selected_lkm">LKM المحددة: %s</string>
<string name="save_log">حفظ السجلات</string>
<string name="log_saved">السجلات محفوظة</string>
<string name="status_supported">Supported</string>
<string name="status_not_supported">Not Supported</string>
<string name="status_unknown">Unknown</string>
<string name="sus_su_mode">SuS SU mode:</string>
<string name="status_supported">إدعمنا</string>
<string name="status_not_supported">غير مدعوم</string>
<string name="status_unknown">غير معروف</string>
<string name="sus_su_mode">وضع SuS SU</string>
<!-- Module related -->
<string name="module_install_confirm">confirm install module %1$s</string>
<string name="unknown_module">unknown module</string>
<string name="module_install_confirm">تأكيد وحدة التثبيت %1$s؟</string>
<string name="unknown_module">وحدة غير معروفة</string>
<!-- Restore related -->
<string name="restore_confirm_title">Confirm Module Restoration</string>
<string name="restore_confirm_message">This operation will overwrite all existing modules. Continue?</string>
<string name="confirm">Confirm</string>
<string name="cancel">Cancel</string>
<string name="restore_confirm_title">تأكيد استعادة الوحدة</string>
<string name="restore_confirm_message">هذه العملية سوف تستبدل جميع الوحدات الموجودة. هل تريد المتابعة؟</string>
<string name="confirm">تأكيد</string>
<string name="cancel">إلغاء</string>
<!-- Backup related -->
<string name="backup_success">Backup successful (tar.gz)</string>
<string name="backup_failed">Backup failed: %1$s</string>
<string name="backup_modules">backup modules</string>
<string name="restore_modules">restore modules</string>
<string name="backup_success">النسخ الاحتياطي ناجح (tar.gz)</string>
<string name="backup_failed">فشل النسخ الاحتياطي: %1$s</string>
<string name="backup_modules">وحدات النسخ الاحتياطي</string>
<string name="restore_modules">استعادة الوحدات</string>
<!-- Restore related messages -->
<string name="restore_success">Modules restored successfully, restart required</string>
<string name="restore_failed">Restore failed: %1$s</string>
<string name="restart_now">Restart Now</string>
<string name="unknown_error">Unknown error</string>
<string name="restore_success">تم استعادة الوحدات بنجاح, إعادة التشغيل مطلوبة</string>
<string name="restore_failed">فشل الاستعادة: %1$s</string>
<string name="restart_now">أعد تشغيل التطبيق الآن</string>
<string name="unknown_error">خطأ غير معروف</string>
<!-- Command related -->
<string name="command_execution_failed">Command execution failed: %1$s</string>
<string name="command_execution_failed">فشل تنفيذ الأوامر: %1$s</string>
<!-- Allowlist related -->
<string name="allowlist_backup_success">Allowlist backup successful</string>
<string name="allowlist_backup_failed">Allowlist backup failed: %1$s</string>
<string name="allowlist_restore_confirm_title">Confirm Allowlist Restoration</string>
<string name="allowlist_restore_confirm_message">This operation will overwrite the current allowlist. Continue?</string>
<string name="allowlist_restore_success">Allowlist restored successfully</string>
<string name="allowlist_restore_failed">Allowlist restore failed: %1$s</string>
<string name="backup_allowlist">Backup Allowlist</string>
<string name="restore_allowlist">Restore Allowlist</string>
<string name="settings_custom_background">Custom App Background</string>
<string name="settings_custom_background_summary">Select an image as background</string>
<string name="settings_card_alpha">Navigation bar transparency</string>
<string name="settings_restore_default">Restore default</string>
<string name="home_android_version">Android version</string>
<string name="home_device_model">Device model</string>
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
<string name="settings_disable_su">Disable su compatibility</string>
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (existing root processes will not be affected).</string>
<string name="using_mksu_manager">You are using the SukiSU Beta manager</string>
<string name="module_install_multiple_confirm">Are you sure you want to install the selected %d modules?</string>
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
<string name="more_settings">More settings</string>
<string name="allowlist_backup_success">السماح بالنسخ الاحتياطي للقائمة بنجاح</string>
<string name="allowlist_backup_failed">فشل النسخ الاحتياطي لقائمة السماح: %1$s</string>
<string name="allowlist_restore_confirm_title">تأكيد استعادة القائمة المسموح بها</string>
<string name="allowlist_restore_confirm_message">هذه العملية ستقوم بالكتابة فوق قائمة المسموح بها. هل تريد المتابعة؟</string>
<string name="allowlist_restore_success">تمت استعادة القائمة بنجاح</string>
<string name="allowlist_restore_failed">فشل استعادة القائمة المسموحة: %1$s</string>
<string name="backup_allowlist">قائمة النسخ الاحتياطي</string>
<string name="restore_allowlist">استعادة قائمة المسموح بها</string>
<string name="settings_custom_background">خلفية التطبيق المخصصة</string>
<string name="settings_custom_background_summary">حدد صورة كخلفية</string>
<string name="settings_card_alpha">شفافية شريط التنقل</string>
<string name="settings_restore_default">استعادة الإعدادات الافتراضية</string>
<string name="home_android_version">‏إصدار Android</string>
<string name="home_device_model">نوع الجهاز</string>
<string name="su_not_allowed">لا يسمح بمنح المستخدم المتميز ل %s</string>
<string name="settings_disable_su">تعطيل توافق su</string>
<string name="settings_disable_su_summary">تعطيل أي تطبيقات مؤقتًا من الحصول على امتيازات الجذر عن طريق الأمر &lt;unk&gt; su (لن تتأثر عمليات الجذر الحالية).</string>
<string name="using_mksu_manager">أنت تستخدم مدير Beta SukiSU</string>
<string name="module_install_multiple_confirm">هل أنت متأكد من أنك تريد تثبيت وحدات %d المحددة؟</string>
<string name="module_install_multiple_confirm_with_names">هل أنت متأكد من أنك تريد تثبيت وحدات %1$d التالية؟ \n\n%2$s</string>
<string name="more_settings">المزيد من الإعدادات</string>
<string name="selinux">SELinux</string>
<string name="selinux_enabled">Enabled</string>
<string name="selinux_disabled">Disabled</string>
<string name="simple_mode">Simplicity mode</string>
<string name="simple_mode_summary">Hides unnecessary cards when turned on</string>
<string name="hide_kernel_kernelsu_version">Hide kernel version</string>
<string name="hide_kernel_kernelsu_version_summary">Hide kernel version</string>
<string name="hide_other_info">Hide other info</string>
<string name="hide_other_info_summary">Hides information about the number of super users, modules and KPM modules on the home page</string>
<string name="hide_susfs_status">Hide SuSFS status</string>
<string name="hide_susfs_status_summary">Hide SuSFS status information on the home page</string>
<string name="hide_link_card">Hide Link Card Status</string>
<string name="hide_link_card_summary">Hide link card information on the home page</string>
<string name="theme_mode">Theme</string>
<string name="theme_follow_system">Follow system</string>
<string name="theme_light">Light</string>
<string name="theme_dark">Dark</string>
<string name="manual_hook">Manual Hook</string>
<string name="dynamic_color_title">Dynamic colours</string>
<string name="dynamic_color_summary">Dynamic colours using system themes</string>
<string name="choose_theme_color">Choose a theme colour</string>
<string name="color_default">Blue</string>
<string name="color_green">Green</string>
<string name="color_purple">Purple</string>
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="horizon_kernel">Install Anykernel3</string>
<string name="horizon_kernel_summary">Flash AnyKernel3 kernel file</string>
<string name="root_required">Requires root privileges</string>
<string name="copy_failed">File Copy Failure</string>
<string name="reboot_complete_title">Scrubbing complete</string>
<string name="reboot_complete_msg">Whether to reboot immediately</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="failed_reboot">Reboot Failed</string>
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="selinux_enabled">مفعّل</string>
<string name="selinux_disabled">رفض</string>
<string name="simple_mode">وضع البساطة</string>
<string name="simple_mode_summary">إخفاء البطاقات غير الضرورية عند تشغيلها</string>
<string name="hide_kernel_kernelsu_version">إخفاء إصدار النواة</string>
<string name="hide_kernel_kernelsu_version_summary">إخفاء إصدار النواة</string>
<string name="hide_other_info">إخفاء معلومات أخرى</string>
<string name="hide_other_info_summary">يخفي معلومات عن عدد المستخدمين المتميزين والوحدات ووحدات KPM على الصفحة الرئيسية</string>
<string name="hide_susfs_status">إخفاء حالة SuSFS</string>
<string name="hide_susfs_status_summary">إخفاء معلومات حالة SuSFS على الصفحة الرئيسية</string>
<string name="hide_link_card">إخفاء حالة بطاقة الرابط</string>
<string name="hide_link_card_summary">إخفاء معلومات البطاقة في الصفحة الرئيسية</string>
<string name="theme_mode">الثيم</string>
<string name="theme_follow_system">اتبّاع النظام</string>
<string name="theme_light">فاتح</string>
<string name="theme_dark">مظلم</string>
<string name="manual_hook">ربط يدوي</string>
<string name="dynamic_color_title">لون ديناميكية</string>
<string name="dynamic_color_summary">الألوان الديناميكية باستخدام سمات النظام</string>
<string name="choose_theme_color">اختر لون السمة</string>
<string name="color_default">أزرق</string>
<string name="color_green">أخضر</string>
<string name="color_purple">أرجواني</string>
<string name="color_orange">برتقالي</string>
<string name="color_pink">وردي</string>
<string name="color_gray">رمادي</string>
<string name="color_yellow">الأصفر</string>
<string name="flash_option">خيارات الفرشاة</string>
<string name="flash_option_tip">حدد الملف المراد إلفاؤه</string>
<string name="horizon_kernel">Anykernel3 yükle</string>
<string name="horizon_kernel_summary">فلاش AnyKernel3 ملف kernel</string>
<string name="root_required">يتطلب امتيازات الجذر</string>
<string name="copy_failed">فشل نسخ الملف</string>
<string name="reboot_complete_title">اكتمل التشويش</string>
<string name="reboot_complete_msg">هل تريد إعادة التشغيل فوراً؟</string>
<string name="yes">نعم</string>
<string name="no">لايوجد</string>
<string name="failed_reboot">فشل إعادة التشغيل</string>
<string name="batch_authorization">التمكين</string>
<string name="batch_cancel_authorization">السحب</string>
<string name="backup">النسخ الاحتياطية</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">Version</string>
<string name="kpm_author">Author</string>
<string name="kpm_uninstall">Uninstall</string>
<string name="kpm_uninstall_success">Uninstalled successfully</string>
<string name="kpm_uninstall_failed">Failed to uninstall</string>
<string name="kpm_install">Install</string>
<string name="kpm_install_success">Load of kpm module successful</string>
<string name="kpm_install_failed">Load of kpm module failed</string>
<string name="kpm_args">Parameters</string>
<string name="kpm_control">Execute</string>
<string name="home_kpm_version">KPM Version</string>
<string name="close_notice">Close</string>
<string name="kernel_module_notice">The following kernel module functions were developed by KernelPatch and modified to include the kernel module functions of SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra Look forward to</string>
<string name="kpm_control_success">Success</string>
<string name="kpm_control_failed">Failed</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
<string name="custom_settings">Custom settings</string>
<string name="kpm_empty">لا توجد وحدات نواة مثبتة في هذا الوقت</string>
<string name="kpm_version">الإصدار</string>
<string name="kpm_author">المؤلف</string>
<string name="kpm_uninstall">إلغاء التثبيت</string>
<string name="kpm_uninstall_success">تم إلغاء التثبيت بنجاح</string>
<string name="kpm_uninstall_failed">فشل في إلغاء التثبيت</string>
<string name="kpm_install">تثبيت</string>
<string name="kpm_install_success">تم تحميل وحدة كيلو جزء بنجاح</string>
<string name="kpm_install_failed">فشل تحميل وحدة كيلو بايم</string>
<string name="kpm_args">العوامل المتغيرة</string>
<string name="kpm_control">تنفيذ</string>
<string name="home_kpm_version">إصدار KPM</string>
<string name="close_notice">إغلاق</string>
<string name="kernel_module_notice">تم تطوير وظائف نواة الوحدة النمطية التالية بواسطة KernelPatch وتعديلها لتشمل وظائف نواة الوحدة النمطية لـ SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">سوكيسو أولترا تتطلع إلى الأمام</string>
<string name="kpm_control_success">نجحت</string>
<string name="kpm_control_failed">فشل</string>
<string name="home_click_to_ContributionCard_kernelsu">وستشكل سوكيسو أولترا في المستقبل فرعا مستقلا نسبيا من فروع الوحدة، ولكننا لا نزال نقدر كيرنيل سو وموكسو الرسميين وما إلى ذلك. لإسهاماتهم!</string>
<string name="not_supported">غير مدعوم</string>
<string name="supported">إدعمنا</string>
<string name="home_kpm_module">"عدد وحدات KPM: %d "</string>
<string name="kpm_invalid_file">ملف KPM غير صحيح</string>
<string name="kernel_patched">النواة غير مصحوبة</string>
<string name="kernel_not_enabled">لم يتم تكوين النواة</string>
<string name="custom_settings">الإعدادات المُخصصة</string>
<string name="kpm_install_mode">KPM Install</string>
<string name="kpm_install_mode_load">Load</string>
<string name="kpm_install_mode_embed">Embed</string>
<string name="kpm_install_mode_description">Please select: %1\$s Module Installation Mode \n\nLoad: Temporarily load the module \nEmbedded: Permanently install into the system</string>
<string name="log_failed_to_check_module_file">Failed to check module file existence</string>
<string name="snackbar_failed_to_check_module_file">Unable to check if module file exists</string>
<string name="confirm_uninstall_title">Confirm uninstallation</string>
<string name="confirm_uninstall_confirm">Uninstall</string>
<string name="confirm_uninstall_dismiss">Cancel</string>
<string name="theme_color">Theme Color</string>
<string name="invalid_file_type">Incorrect file type! Please select .kpm file.</string>
<string name="confirm_uninstall_title_with_filename">Uninstall</string>
<string name="confirm_uninstall_content">The following KPM will be uninstalled: %s</string>
<string name="settings_susfs_toggle_summary">Disable kprobe hooks created by KernelSU, using inline hooks instead, which is similar to non-GKI kernel hooking method.</string>
<string name="image_editor_title">Adjust background image</string>
<string name="image_editor_hint">Use two fingers to zoom the image, and one finger to drag it to adjust the position</string>
<string name="background_image_error">Could not load image</string>
<string name="reprovision">Reprovision</string>
<string name="kpm_install_mode_load">التحميل</string>
<string name="kpm_install_mode_embed">فسيفساء</string>
<string name="kpm_install_mode_description">الرجاء التحديد: %1\$s وضع تثبيت الوحدة \n\nالتحميل: قم بتحميل الوحدة \nمؤقتا: تثبيت دائم في النظام</string>
<string name="log_failed_to_check_module_file">فشل التحقق من وجود ملف الوحدة</string>
<string name="snackbar_failed_to_check_module_file">غير قادر على التحقق من وجود ملف الوحدة</string>
<string name="confirm_uninstall_title">تأكيد إلغاء التثبيت</string>
<string name="confirm_uninstall_confirm">إلغاء التثبيت</string>
<string name="confirm_uninstall_dismiss">إلغاء</string>
<string name="theme_color">ألوان المظهر</string>
<string name="invalid_file_type">نوع الملف غير صحيح! الرجاء تحديد ملف .kpm.</string>
<string name="confirm_uninstall_title_with_filename">إلغاء التثبيت</string>
<string name="confirm_uninstall_content">سيتم إلغاء تثبيت KPM التالية: %s</string>
<string name="settings_susfs_toggle_summary">تعطيل روابط kprobe التي أنشأتها KernelSU، باستخدام الروابط الواردة بدلاً من ذلك، والتي تشبه طريقة الربط غير GKI غير GKI.</string>
<string name="image_editor_title">ضبط صورة الخلفية</string>
<string name="image_editor_hint">استخدم إصبعين لتكبير الصورة، وأصبع واحد لسحبها لضبط الموضع</string>
<string name="background_image_error">تعذر تحميل الصورة</string>
<string name="reprovision">إعادة</string>
<!-- Kernel Flash Progress Related -->
<string name="horizon_flash_title">Kernel Flashing</string>
<string name="horizon_logs_label">Logs:</string>
<string name="horizon_flash_complete">Flash Complete</string>
<string name="horizon_flash_title">ضرب النواة</string>
<string name="horizon_logs_label">السجلات:</string>
<string name="horizon_flash_complete">الفلاش اكتمل</string>
<!-- Flash Status Related -->
<string name="horizon_preparing">Preparing…</string>
<string name="horizon_cleaning_files">Cleaning files</string>
<string name="horizon_copying_files">Copying files</string>
<string name="horizon_extracting_tool">Extracting flash tool</string>
<string name="horizon_patching_script">Patching flash script</string>
<string name="horizon_flashing">Flashing kernel</string>
<string name="horizon_flash_complete_status">Flash completed</string>
<string name="horizon_preparing">جار التحضير</string>
<string name="horizon_cleaning_files">تنظيف الملفات</string>
<string name="horizon_copying_files">جارٍ نسخ الملف</string>
<string name="horizon_extracting_tool">استخراج أداة فلاش</string>
<string name="horizon_patching_script">تعديل البرنامج النصي الفلاش</string>
<string name="horizon_flashing">نواة رمادية</string>
<string name="horizon_flash_complete_status">الفلاش اكتمل</string>
<!-- Slot selection related strings -->
<string name="select_slot_title">Select Flash Slot</string>
<string name="select_slot_description">Please select the target slot for flashing boot</string>
<string name="slot_a">Slot A</string>
<string name="slot_b">Slot B</string>
<string name="selected_slot">Selected slot: %1$s</string>
<string name="horizon_getting_original_slot">Getting the original slot</string>
<string name="horizon_setting_target_slot">Setting the specified slot</string>
<string name="horizon_restoring_original_slot">Restore Default Slot</string>
<string name="current_slot">Current Slot%1$s </string>
<string name="select_slot_title">حدد فتحة الفلاش</string>
<string name="select_slot_description">الرجاء تحديد الخانة المستهدفة للتشغيل المبطن</string>
<string name="slot_a">خانة A</string>
<string name="slot_b">الخانة B</string>
<string name="selected_slot">الفتحة المحددة: %1$s</string>
<string name="horizon_getting_original_slot">الحصول على الفتحة الأصلية</string>
<string name="horizon_setting_target_slot">تعيين الفتحة المحددة</string>
<string name="horizon_restoring_original_slot">استعادة الخانة الافتراضية</string>
<string name="current_slot">فتحة النظام الحالية الافتراضية%1$s </string>
<!-- Error Messages -->
<string name="horizon_copy_failed">Copy failed</string>
<string name="horizon_unknown_error">Unknown error</string>
<string name="flash_failed_message">Flash failed</string>
<string name="horizon_copy_failed">فشل النسخ</string>
<string name="horizon_unknown_error">خطأ غير معروف</string>
<string name="flash_failed_message">فشل التركيب</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<string name="Lkm_install_methods">إصلاح/تثبيت LKM</string>
<string name="GKI_install_methods">نواة رمادية</string>
<string name="kernel_version_log">إصدار النواة%1$s</string>
<string name="tool_version_log">استخدام أداة التصحيح%1$s</string>
<string name="configuration">تعيين</string>
<string name="app_settings">إعدادات التطبيق </string>
<string name="tools">ادوات</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
<string name="apps_with_custom_profile">Applications with customized configurations</string>
<string name="other_apps">Applications with unchanged defaults</string>
<string name="no_apps_found">Application not found</string>
<string name="selinux_enabled_toast">SELinux Enabled</string>
<string name="selinux_disabled_toast">SELinux Disabled</string>
<string name="selinux_change_failed">SELinux Status change failed</string>
<string name="advanced_settings">Advanced Settings</string>
<string name="appearance_settings">Customize the toolbar</string>
<string name="back">Comeback</string>
<string name="expand">Be in full swing</string>
<string name="collapse">put away</string>
<string name="susfs_enabled">SuSFS enabled</string>
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="root_require_for_install">Requires root privileges</string>
<string name="clear">إزالة</string>
<string name="apps_with_root">التطبيقات مع امتيازات الجذر</string>
<string name="apps_with_custom_profile">التطبيقات مع الإعدادات المخصصة</string>
<string name="other_apps">التطبيقات ذات الإعدادات الافتراضية بدون تغيير</string>
<string name="no_apps_found">التطبيق غير موجود</string>
<string name="selinux_enabled_toast">تم تمكين SELinux</string>
<string name="selinux_disabled_toast">تم تعطيل SELinux</string>
<string name="selinux_change_failed">فشل تغيير حالة SELinux</string>
<string name="advanced_settings">إعدادات متقدمة</string>
<string name="appearance_settings">تخصيص شريط الأدوات</string>
<string name="back">عد مرة أخرى</string>
<string name="expand">كن في طريقه كامل</string>
<string name="collapse">وضع بعيدا</string>
<string name="susfs_enabled">تم تمكين SuSFS</string>
<string name="susfs_disabled">تم تعطيل SuSFS</string>
<string name="background_set_success">تم تعيين الخلفية بنجاح</string>
<string name="background_removed">إزالة خلفيات مخصصة</string>
<string name="icon_switch_title">Alternate icon</string>
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon.</string>
<string name="icon_switched">Icon switched</string>
<string name="root_require_for_install">يتطلب امتيازات الجذر</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<string name="show_kpm_info">عرض وظيفة KPM</string>
<string name="show_kpm_info_summary">إخفاء معلومات KPM ووظيفتها في الشريط المنزلي والأسفل</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<string name="use_webuix">حدد محرك WebUI لاستخدامه</string>
<string name="engine_auto_select">اختيار تلقائي</string>
<string name="engine_force_webuix">Force the use of WebUI X</string>
<string name="engine_force_ksu">Mandatory use of KSU WebUI</string>
<string name="use_webuix_eruda">حقن Eruda في WebUI X</string>
<string name="use_webuix_eruda_summary">حقن وحدة التصحيح في WebUI X لجعل تصحيح الأخطاء أسهل. يتطلب تصحيح أخطاء الويب لتكون قيد التشغيل.</string>
<!-- DPI setting related strings -->
<string name="dpi_settings">DPI setting</string>
<string name="app_dpi_title">Applied DPI</string>
<string name="app_dpi_summary">Adjust the screen display density for the current application only</string>
<string name="dpi_size_small">Small </string>
<string name="dpi_size_medium">Medium </string>
<string name="dpi_size_large">Big</string>
<string name="dpi_size_extra_large">oversize</string>
<string name="dpi_size_custom">customizable</string>
<string name="dpi_apply_settings">Applying DPI settings</string>
<string name="dpi_confirm_title">Confirm DPI change</string>
<string name="dpi_confirm_message">Are you sure you want to change the application DPI from %1$d to %2$d?</string>
<string name="dpi_confirm_summary">Application needs to be restarted to apply the new DPI settings, does not affect the system status bar or other applications</string>
<string name="dpi_applied_success">DPI has been set to %1$d, effective after restarting the application</string>
<string name="dpi_settings">إعداد DPI</string>
<string name="app_dpi_title">تم تطبيق DPI</string>
<string name="app_dpi_summary">ضبط كثافة عرض الشاشة للتطبيق الحالي فقط</string>
<string name="dpi_size_small">صغير </string>
<string name="dpi_size_medium">متوسط </string>
<string name="dpi_size_large">كبير</string>
<string name="dpi_size_extra_large">حجم كبير</string>
<string name="dpi_size_custom">قابلة للتعديل</string>
<string name="dpi_apply_settings">تطبيق إعدادات DPI</string>
<string name="dpi_confirm_title">تأكيد تغيير إدارة شؤون الإعلام</string>
<string name="dpi_confirm_message">هل أنت متأكد من أنك تريد تغيير تطبيق DPI من %1$d إلى %2$d؟</string>
<string name="dpi_confirm_summary">يحتاج التطبيق إلى إعادة تشغيل لتطبيق الإعدادات الجديدة لإدارة شؤون الإعلام، ولا يؤثر على شريط حالة النظام أو التطبيقات الأخرى</string>
<string name="dpi_applied_success">تم تعيين DPI إلى %1$d، فعلي بعد إعادة تشغيل التطبيق</string>
<!-- Language settings related strings -->
<string name="language_setting">App Language</string>
<string name="language_follow_system">Follow System</string>
<string name="language_changed">Language changed, restarting to apply changes</string>
<string name="settings_card_dim">Card Darkness Adjustment</string>
<string name="language_setting">لغة التطبيق</string>
<string name="language_follow_system">اتبع النظام</string>
<string name="language_changed">تم تغيير اللغة، إعادة التشغيل لتطبيق التغييرات</string>
<string name="settings_card_dim">تعديل ظلام البطاقة</string>
<!-- Super User Related -->
<string name="scroll_to_top">Top</string>
<string name="scroll_to_bottom">Bottom</string>
<string name="scroll_to_top_description">Scroll to top</string>
<string name="scroll_to_bottom_description">Scroll to the bottom</string>
<string name="authorized">authorized</string>
<string name="unauthorized">unauthorized</string>
<string name="selected">Selected</string>
<string name="select">option</string>
<string name="profile_umount_modules_disable">Disable custom uninstallation module</string>
<string name="scroll_to_top">في الأعلى</string>
<string name="scroll_to_bottom">أسفل</string>
<string name="scroll_to_top_description">التمرير لأعلى</string>
<string name="scroll_to_bottom_description">التمرير إلى الأسفل</string>
<string name="authorized">مصرح</string>
<string name="unauthorized">غير مصرح</string>
<string name="selected">محدد</string>
<string name="select">خيار</string>
<string name="profile_umount_modules_disable">تعطيل وحدة إلغاء التثبيت المخصصة</string>
<!-- Flash related -->
<string name="error_code">error code</string>
<string name="check_log">Please check the log</string>
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="error_code">رمز الخطأ</string>
<string name="check_log">يرجى التحقق من السجل</string>
<string name="installing_module">تم تثبيت الوحدة %1$d/%2$d</string>
<string name="module_failed_count">أخفق %d في تثبيت وحدة جديدة</string>
<string name="module_download_error">فشل تحميل الوحدة</string>
<string name="kernel_flashing">ضرب النواة</string>
</resources>

View File

@@ -113,6 +113,7 @@
<string name="grant_root_failed">Failed to grant root!</string>
<string name="action">Action</string>
<string name="open">Open</string>
<string name="close">Close</string>
<string name="enable_web_debugging">Enable WebView debugging</string>
<string name="enable_web_debugging_summary">Can be used to debug WebUI. Please enable only when needed.</string>
<string name="direct_install">Direct install (Recommended)</string>
@@ -208,7 +209,7 @@
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="color_yellow">Yellow</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="horizon_kernel">Install Anykernel3</string>
@@ -223,7 +224,6 @@
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">Version</string>
@@ -245,7 +245,7 @@
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="home_kpm_module">"Number of KPMs: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
@@ -296,13 +296,12 @@
<string name="flash_failed_message">Flash failed</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="GKI_install_methods">Flashing AnyKernel3</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
@@ -321,13 +320,18 @@
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="icon_switch_title">Alternate icon</string>
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon.</string>
<string name="icon_switched">Icon switched</string>
<string name="root_require_for_install">Requires root privileges</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix">Select the WebUI engine to use</string>
<string name="engine_auto_select">Automatic Selection</string>
<string name="engine_force_webuix">Force the use of WebUI X</string>
<string name="engine_force_ksu">Mandatory use of KSU WebUI</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<!-- DPI setting related strings -->
@@ -365,4 +369,5 @@
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="kernel_flashing">Kernel Flashing</string>
</resources>

View File

@@ -113,6 +113,7 @@
<string name="grant_root_failed">Failed to grant root!</string>
<string name="action">Action</string>
<string name="open">Open</string>
<string name="close">Close</string>
<string name="enable_web_debugging">Enable WebView debugging</string>
<string name="enable_web_debugging_summary">Can be used to debug WebUI. Please enable only when needed.</string>
<string name="direct_install">Direct install (Recommended)</string>
@@ -208,7 +209,7 @@
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="color_yellow">Yellow</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="horizon_kernel">Install Anykernel3</string>
@@ -223,7 +224,6 @@
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">Version</string>
@@ -245,7 +245,7 @@
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="home_kpm_module">"Number of KPMs: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
@@ -296,13 +296,12 @@
<string name="flash_failed_message">Flash failed</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="GKI_install_methods">Flashing AnyKernel3</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
@@ -321,13 +320,18 @@
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="icon_switch_title">Alternate icon</string>
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon.</string>
<string name="icon_switched">Icon switched</string>
<string name="root_require_for_install">Requires root privileges</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix">Select the WebUI engine to use</string>
<string name="engine_auto_select">Automatic Selection</string>
<string name="engine_force_webuix">Force the use of WebUI X</string>
<string name="engine_force_ksu">Mandatory use of KSU WebUI</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<!-- DPI setting related strings -->
@@ -365,4 +369,5 @@
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="kernel_flashing">Kernel Flashing</string>
</resources>

View File

@@ -113,6 +113,7 @@
<string name="grant_root_failed">Failed to grant root!</string>
<string name="action">Action</string>
<string name="open">Open</string>
<string name="close">Close</string>
<string name="enable_web_debugging">Enable WebView debugging</string>
<string name="enable_web_debugging_summary">Can be used to debug WebUI. Please enable only when needed.</string>
<string name="direct_install">Direct install (Recommended)</string>
@@ -208,7 +209,7 @@
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="color_yellow">Yellow</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="horizon_kernel">Install Anykernel3</string>
@@ -223,7 +224,6 @@
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">Version</string>
@@ -245,7 +245,7 @@
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="home_kpm_module">"Number of KPMs: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
@@ -296,13 +296,12 @@
<string name="flash_failed_message">Flash failed</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="GKI_install_methods">Flashing AnyKernel3</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
@@ -321,13 +320,18 @@
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="icon_switch_title">Alternate icon</string>
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon.</string>
<string name="icon_switched">Icon switched</string>
<string name="root_require_for_install">Requires root privileges</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix">Select the WebUI engine to use</string>
<string name="engine_auto_select">Automatic Selection</string>
<string name="engine_force_webuix">Force the use of WebUI X</string>
<string name="engine_force_ksu">Mandatory use of KSU WebUI</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<!-- DPI setting related strings -->
@@ -365,4 +369,5 @@
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="kernel_flashing">Kernel Flashing</string>
</resources>

View File

@@ -11,7 +11,7 @@
<string name="home_unsupported_reason">KernelSU unterstützt derzeit nur GKI-Kernel</string>
<string name="home_kernel">Kernel</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">SuSFS Version</string>
<string name="home_susfs_version">SuSFS version</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_manager_version">Manager-Version</string>
<string name="home_fingerprint">Fingerabdruck</string>
@@ -25,10 +25,10 @@
<string name="module_failed_to_disable">Moduldeaktivierung fehlgeschlagen: %s</string>
<string name="module_empty">Keine Modul installiert</string>
<string name="module">Modul</string>
<string name="module_sort_action_first">Sort (Action first)</string>
<string name="module_sort_enabled_first">Sort (Enabled first)</string>
<string name="module_sort_action_first">Sortiere zuerst (Aktion)</string>
<string name="module_sort_enabled_first">Sortieren (zuerst aktiviert)</string>
<string name="uninstall">Deinstallieren</string>
<string name="restore">Restore</string>
<string name="restore">Wiederherstellen</string>
<string name="module_install">Installieren</string>
<string name="install">Installieren</string>
<string name="reboot">Neustarten</string>
@@ -56,7 +56,7 @@
<string name="home_click_to_learn_kernelsu">Erfahre, wie KernelSU installiert wird und wie Module verwendet werden</string>
<string name="home_support_title">Unterstütze uns</string>
<string name="home_support_content">KernelSU ist und wird immer frei und quelloffen sein. Du kannst uns jedoch deine Unterstützung zeigen, indem du eine Spende tätigst.</string>
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
<string name="about_source_code"><![CDATA[Den Quellcode finden Sie unter %1$s<br/>Begleiten Sie uns %2$s-Kanal]]></string>
<string name="profile_default">Standard</string>
<string name="profile_template">Vorlage</string>
<string name="profile_custom">Benutzerdefiniert</string>
@@ -70,10 +70,10 @@
<string name="profile_selinux_context">SELinux-Kontext</string>
<string name="profile_umount_modules">Module aushängen</string>
<string name="failed_to_update_app_profile">App-Profilaktualisierung für %s fehlgeschlagen</string>
<string name="require_kernel_version" formatted="false">The current KernelSU version %d is too low for the manager to work properly. Please upgrade to version %d or higher!</string>
<string name="require_kernel_version" formatted="false">Die aktuelle KernelSU-Version %d ist zu alt für diese Manager-Version. Bitte auf Version %d oder höher aktualisieren!</string>
<string name="settings_umount_modules_default">Module standardmäßig aushängen</string>
<string name="settings_umount_modules_default_summary">Globaler Standardwert für \"Module aushängen\" im App-Profil. Falls er aktiviert ist, werden alle Moduländerungen im System für alle Apps entfernt, für die kein Profil festgelegt ist.</string>
<string name="settings_susfs_toggle">Disable kprobe hooks</string>
<string name="settings_susfs_toggle">Kprobe-Hooks deaktivieren</string>
<string name="profile_umount_modules_summary">Wenn du diese Option aktivierst, kann KernelSU alle von den Modulen für diese App geänderten Dateien wiederherstellen.</string>
<string name="profile_selinux_domain">Domäne</string>
<string name="profile_selinux_rules">Regeln</string>
@@ -113,6 +113,7 @@
<string name="grant_root_failed">Root-Zugriff konnte nicht gewährt werden!</string>
<string name="action">Aktion</string>
<string name="open">Öffnen</string>
<string name="close">Schließen</string>
<string name="enable_web_debugging">WebView-Debugging aktivieren</string>
<string name="enable_web_debugging_summary">Kann zum Fehlerbeheben der WebUI verwendet werden, bitte nur im Notfall aktivieren.</string>
<string name="direct_install">Direkte Installation (empfohlen)</string>
@@ -137,234 +138,238 @@
<string name="selected_lkm">Wähle LKM: %s</string>
<string name="save_log">Protokolle Speichern</string>
<string name="log_saved">Protokolle gespeichert</string>
<string name="status_supported">Supported</string>
<string name="status_not_supported">Not Supported</string>
<string name="status_unknown">Unknown</string>
<string name="sus_su_mode">SuS SU mode:</string>
<string name="status_supported">Unterstützt:</string>
<string name="status_not_supported">Nicht unterstützt</string>
<string name="status_unknown">Unbekannt</string>
<string name="sus_su_mode">SuS SU-Modus:</string>
<!-- Module related -->
<string name="module_install_confirm">confirm install module %1$s</string>
<string name="unknown_module">unknown module</string>
<string name="module_install_confirm">das Installationsmodul %1$s bestätigen ?</string>
<string name="unknown_module">unbekannter Modul</string>
<!-- Restore related -->
<string name="restore_confirm_title">Confirm Module Restoration</string>
<string name="restore_confirm_message">This operation will overwrite all existing modules. Continue?</string>
<string name="confirm">Confirm</string>
<string name="cancel">Cancel</string>
<string name="restore_confirm_title">Modul-Wiederherstellung bestätigen</string>
<string name="restore_confirm_message">Diese Operation wird alle vorhandenen Module überschreiben. Fortfahren?</string>
<string name="confirm">Bestätigen</string>
<string name="cancel">Abbrechen</string>
<!-- Backup related -->
<string name="backup_success">Backup successful (tar.gz)</string>
<string name="backup_failed">Backup failed: %1$s</string>
<string name="backup_modules">backup modules</string>
<string name="restore_modules">restore modules</string>
<string name="backup_success">Sicherung erfolgreich (tar.gz)</string>
<string name="backup_failed">Sicherung fehlgeschlagen: %1$s</string>
<string name="backup_modules">sicherungsmodule</string>
<string name="restore_modules">wiederherstellen</string>
<!-- Restore related messages -->
<string name="restore_success">Modules restored successfully, restart required</string>
<string name="restore_failed">Restore failed: %1$s</string>
<string name="restart_now">Restart Now</string>
<string name="unknown_error">Unknown error</string>
<string name="restore_success">Module erfolgreich wiederhergestellt, Neustart erforderlich</string>
<string name="restore_failed">Wiederherstellung fehlgeschlagen: %1$s</string>
<string name="restart_now">Jetzt Neustarten</string>
<string name="unknown_error">Ein unbekannter Fehler ist aufgetreten</string>
<!-- Command related -->
<string name="command_execution_failed">Command execution failed: %1$s</string>
<string name="command_execution_failed">Befehlsausführung fehlgeschlagen: %1$s</string>
<!-- Allowlist related -->
<string name="allowlist_backup_success">Allowlist backup successful</string>
<string name="allowlist_backup_failed">Allowlist backup failed: %1$s</string>
<string name="allowlist_restore_confirm_title">Confirm Allowlist Restoration</string>
<string name="allowlist_restore_confirm_message">This operation will overwrite the current allowlist. Continue?</string>
<string name="allowlist_restore_success">Allowlist restored successfully</string>
<string name="allowlist_restore_failed">Allowlist restore failed: %1$s</string>
<string name="backup_allowlist">Backup Allowlist</string>
<string name="restore_allowlist">Restore Allowlist</string>
<string name="settings_custom_background">Custom App Background</string>
<string name="settings_custom_background_summary">Select an image as background</string>
<string name="settings_card_alpha">Navigation bar transparency</string>
<string name="settings_restore_default">Restore default</string>
<string name="home_android_version">Android version</string>
<string name="home_device_model">Device model</string>
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
<string name="settings_disable_su">Disable su compatibility</string>
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (existing root processes will not be affected).</string>
<string name="using_mksu_manager">You are using the SukiSU Beta manager</string>
<string name="module_install_multiple_confirm">Are you sure you want to install the selected %d modules?</string>
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
<string name="more_settings">More settings</string>
<string name="allowlist_backup_success">Sicherung erfolgreich erlaubt</string>
<string name="allowlist_backup_failed">Sicherung der erlaubten Liste fehlgeschlagen: %1$s</string>
<string name="allowlist_restore_confirm_title">Allowlist-Wiederherstellung bestätigen</string>
<string name="allowlist_restore_confirm_message">Dieser Vorgang wird die aktuelle Berechtigungsliste überschreiben. Fortfahren?</string>
<string name="allowlist_restore_success">Liste erfolgreich wiederhergestellt</string>
<string name="allowlist_restore_failed">Wiederherstellung der erlaubten Liste fehlgeschlagen: %1$s</string>
<string name="backup_allowlist">Sicherungsliste</string>
<string name="restore_allowlist">Allowlist wiederherstellen</string>
<string name="settings_custom_background">Eigener App-Hintergrund</string>
<string name="settings_custom_background_summary">Wählen Sie ein Bild als Hintergrund</string>
<string name="settings_card_alpha">Transparenz der Navigationsleiste</string>
<string name="settings_restore_default">Standard wiederherstellen</string>
<string name="home_android_version">Androidversion</string>
<string name="home_device_model">Geräteausführung</string>
<string name="su_not_allowed">Superuser %s zu erlauben ist nicht erlaubt</string>
<string name="settings_disable_su">Su Kompatibilität deaktivieren</string>
<string name="settings_disable_su_summary">Deaktivieren Sie temporär alle Anwendungen, die root-Privilegien über den Befehl &lt;unk&gt; su zu erhalten (bestehende root-Prozesse werden nicht beeinflusst).</string>
<string name="using_mksu_manager">Sie verwenden den SukiSU Beta-Manager</string>
<string name="module_install_multiple_confirm">Sind Sie sicher, dass Sie die ausgewählten %d -Module installieren möchten?</string>
<string name="module_install_multiple_confirm_with_names">Möchten Sie die folgenden %1$d Module installieren? \n\n\n%2$s</string>
<string name="more_settings">Weitere Einstellungen</string>
<string name="selinux">SELinux</string>
<string name="selinux_enabled">Enabled</string>
<string name="selinux_disabled">Disabled</string>
<string name="simple_mode">Simplicity mode</string>
<string name="simple_mode_summary">Hides unnecessary cards when turned on</string>
<string name="hide_kernel_kernelsu_version">Hide kernel version</string>
<string name="hide_kernel_kernelsu_version_summary">Hide kernel version</string>
<string name="hide_other_info">Hide other info</string>
<string name="hide_other_info_summary">Hides information about the number of super users, modules and KPM modules on the home page</string>
<string name="hide_susfs_status">Hide SuSFS status</string>
<string name="hide_susfs_status_summary">Hide SuSFS status information on the home page</string>
<string name="hide_link_card">Hide Link Card Status</string>
<string name="hide_link_card_summary">Hide link card information on the home page</string>
<string name="theme_mode">Theme</string>
<string name="theme_follow_system">Follow system</string>
<string name="theme_light">Light</string>
<string name="theme_dark">Dark</string>
<string name="manual_hook">Manual Hook</string>
<string name="dynamic_color_title">Dynamic colours</string>
<string name="dynamic_color_summary">Dynamic colours using system themes</string>
<string name="choose_theme_color">Choose a theme colour</string>
<string name="color_default">Blue</string>
<string name="color_green">Green</string>
<string name="color_purple">Purple</string>
<string name="selinux_enabled">Aktiviert</string>
<string name="selinux_disabled">Deaktiviert</string>
<string name="simple_mode">Einfachheit Modus</string>
<string name="simple_mode_summary">Versteckt unnötige Karten beim Einschalten</string>
<string name="hide_kernel_kernelsu_version">Kernel-Version ausblenden</string>
<string name="hide_kernel_kernelsu_version_summary">Kernel-Version ausblenden</string>
<string name="hide_other_info">Andere Infos ausblenden</string>
<string name="hide_other_info_summary">Versteckt Informationen über die Anzahl der Supernutzer, Module und KPM-Module auf der Startseite</string>
<string name="hide_susfs_status">SuSFS-Status ausblenden</string>
<string name="hide_susfs_status_summary">SuSFS Statusinformationen auf der Startseite ausblenden</string>
<string name="hide_link_card">Link-Kartenstatus ausblenden</string>
<string name="hide_link_card_summary">Link Karteninformationen auf der Startseite ausblenden</string>
<string name="theme_mode">Thema</string>
<string name="theme_follow_system">Systemkonform</string>
<string name="theme_light">Licht</string>
<string name="theme_dark">Dunkel</string>
<string name="manual_hook">Manueller Hook</string>
<string name="dynamic_color_title">Dynamische Farbe</string>
<string name="dynamic_color_summary">Dynamische Farben mit System-Themes</string>
<string name="choose_theme_color">Wähle eine Theme-Farbe</string>
<string name="color_default">Blau</string>
<string name="color_green">Grün</string>
<string name="color_purple">Lila</string>
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="color_gray">Grau</string>
<string name="color_yellow">Gelb</string>
<string name="flash_option">Pinseloptionen</string>
<string name="flash_option_tip">Wählen Sie die zu flashende Datei</string>
<string name="horizon_kernel">Install Anykernel3</string>
<string name="horizon_kernel_summary">Flash AnyKernel3 kernel file</string>
<string name="root_required">Requires root privileges</string>
<string name="copy_failed">File Copy Failure</string>
<string name="reboot_complete_title">Scrubbing complete</string>
<string name="reboot_complete_msg">Whether to reboot immediately</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="failed_reboot">Reboot Failed</string>
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="horizon_kernel_summary">Flash AnyKernel3 Kernel-Datei</string>
<string name="root_required">Erfordert Root-Rechte</string>
<string name="copy_failed">Datei-Kopierfehler</string>
<string name="reboot_complete_title">Scrubbing abgeschlossen</string>
<string name="reboot_complete_msg">Ob sofort neu gestartet werden soll?</string>
<string name="yes">Ja</string>
<string name="no">Nein</string>
<string name="failed_reboot">Neustart fehlgeschlagen</string>
<string name="batch_authorization">empowern</string>
<string name="batch_cancel_authorization">abheben</string>
<string name="backup">Sicherung</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_empty">Keine installierten Kernelmodule</string>
<string name="kpm_version">Version</string>
<string name="kpm_author">Author</string>
<string name="kpm_uninstall">Uninstall</string>
<string name="kpm_uninstall_success">Uninstalled successfully</string>
<string name="kpm_uninstall_failed">Failed to uninstall</string>
<string name="kpm_install">Install</string>
<string name="kpm_install_success">Load of kpm module successful</string>
<string name="kpm_install_failed">Load of kpm module failed</string>
<string name="kpm_args">Parameters</string>
<string name="kpm_control">Execute</string>
<string name="home_kpm_version">KPM Version</string>
<string name="close_notice">Close</string>
<string name="kernel_module_notice">The following kernel module functions were developed by KernelPatch and modified to include the kernel module functions of SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra Look forward to</string>
<string name="kpm_control_success">Success</string>
<string name="kpm_control_failed">Failed</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
<string name="custom_settings">Custom settings</string>
<string name="kpm_author">Autor</string>
<string name="kpm_uninstall">Deinstallieren</string>
<string name="kpm_uninstall_success">Erfolgreich deinstalliert</string>
<string name="kpm_uninstall_failed">Deinstallation fehlgeschlagen</string>
<string name="kpm_install">Installieren</string>
<string name="kpm_install_success">Laden des kpm Moduls erfolgreich</string>
<string name="kpm_install_failed">Laden des kpm-Moduls fehlgeschlagen</string>
<string name="kpm_args">Parameter</string>
<string name="kpm_control">Ausführen</string>
<string name="home_kpm_version">KPM-Version</string>
<string name="close_notice">Schließen</string>
<string name="kernel_module_notice">Die folgenden Kernel-Modulfunktionen wurden von KernelPatch entwickelt und so modifiziert, dass die Funktionen des Kernel-Moduls von SukiSU Ultra enthalten sind</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra freut sich auf</string>
<string name="kpm_control_success">Erfolgreich</string>
<string name="kpm_control_failed">Fehlgeschlagen</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra wird in Zukunft ein relativ unabhängiger Zweig der KSU sein, aber wir schätzen immer noch die offiziellen KernelSU und MKSU usw. für ihre Beiträge!</string>
<string name="not_supported">Nicht unterstützt</string>
<string name="supported">Unterstützt:</string>
<string name="home_kpm_module">"Anzahl der KPM-Module: %d "</string>
<string name="kpm_invalid_file">Ungültige KPM-Datei</string>
<string name="kernel_patched">Kernel nicht gepatcht</string>
<string name="kernel_not_enabled">Kernel nicht konfiguriert</string>
<string name="custom_settings">Eigene Einstellungen</string>
<string name="kpm_install_mode">KPM Install</string>
<string name="kpm_install_mode_load">Load</string>
<string name="kpm_install_mode_embed">Embed</string>
<string name="kpm_install_mode_description">Please select: %1\$s Module Installation Mode \n\nLoad: Temporarily load the module \nEmbedded: Permanently install into the system</string>
<string name="log_failed_to_check_module_file">Failed to check module file existence</string>
<string name="snackbar_failed_to_check_module_file">Unable to check if module file exists</string>
<string name="confirm_uninstall_title">Confirm uninstallation</string>
<string name="confirm_uninstall_confirm">Uninstall</string>
<string name="confirm_uninstall_dismiss">Cancel</string>
<string name="theme_color">Theme Color</string>
<string name="invalid_file_type">Incorrect file type! Please select .kpm file.</string>
<string name="confirm_uninstall_title_with_filename">Uninstall</string>
<string name="confirm_uninstall_content">The following KPM will be uninstalled: %s</string>
<string name="settings_susfs_toggle_summary">Disable kprobe hooks created by KernelSU, using inline hooks instead, which is similar to non-GKI kernel hooking method.</string>
<string name="image_editor_title">Adjust background image</string>
<string name="image_editor_hint">Use two fingers to zoom the image, and one finger to drag it to adjust the position</string>
<string name="background_image_error">Could not load image</string>
<string name="reprovision">Reprovision</string>
<string name="kpm_install_mode_load">Laden</string>
<string name="kpm_install_mode_embed">Einbetten</string>
<string name="kpm_install_mode_description">Bitte wählen: %1\$s Modul-Installationsmodus \n\nLaden: Das Modul \ntemporär laden: Dauerhaft in das System installieren</string>
<string name="log_failed_to_check_module_file">Fehler beim Prüfen der Moduldatei-Existenz</string>
<string name="snackbar_failed_to_check_module_file">Kann nicht überprüfen, ob die Moduldatei existiert</string>
<string name="confirm_uninstall_title">Deinstallation bestätigen.</string>
<string name="confirm_uninstall_confirm">Deinstallieren</string>
<string name="confirm_uninstall_dismiss">Abbrechen</string>
<string name="theme_color">Themenfarbe</string>
<string name="invalid_file_type">Falscher Dateityp! Bitte wählen Sie eine .kpm Datei.</string>
<string name="confirm_uninstall_title_with_filename">Deinstallieren</string>
<string name="confirm_uninstall_content">Folgende KPM wird deinstalliert: %s</string>
<string name="settings_susfs_toggle_summary">Deaktiviere kprobe Hooks die von KernelSU erstellt wurden und stattdessen inline Hooks verwenden, was der Nicht-GKI-Kernel-Hooking Methode ähnlich ist.</string>
<string name="image_editor_title">Hintergrundbild anpassen</string>
<string name="image_editor_hint">Verwende zwei Finger um das Bild zu vergrößern und einen Finger um die Position anzupassen</string>
<string name="background_image_error">Bild konnte nicht geladen werden</string>
<string name="reprovision">Rückzahlung</string>
<!-- Kernel Flash Progress Related -->
<string name="horizon_flash_title">Kernel Flashing</string>
<string name="horizon_flash_title">Kernel-Flashen</string>
<string name="horizon_logs_label">Logs:</string>
<string name="horizon_flash_complete">Flash Complete</string>
<string name="horizon_flash_complete">Blitz abgeschlossen</string>
<!-- Flash Status Related -->
<string name="horizon_preparing">Preparing</string>
<string name="horizon_cleaning_files">Cleaning files</string>
<string name="horizon_copying_files">Copying files</string>
<string name="horizon_extracting_tool">Extracting flash tool…</string>
<string name="horizon_patching_script">Patching flash script…</string>
<string name="horizon_flashing">Flashing kernel…</string>
<string name="horizon_flash_complete_status">Flash completed</string>
<string name="horizon_preparing">Vorbereiten</string>
<string name="horizon_cleaning_files">Bereinigung von Dateien</string>
<string name="horizon_copying_files">Kopiere Datei</string>
<string name="horizon_extracting_tool">Entpacken des Flash-Tools</string>
<string name="horizon_patching_script">Patcht Flash-Skript…</string>
<string name="horizon_flashing">Flashen des Kernels</string>
<string name="horizon_flash_complete_status">Blitz abgeschlossen</string>
<!-- Slot selection related strings -->
<string name="select_slot_title">Select Flash Slot</string>
<string name="select_slot_description">Please select the target slot for flashing boot</string>
<string name="select_slot_title">Wähle Flash Slot</string>
<string name="select_slot_description">Bitte wählen Sie den Ziel-Slot zum Blinken des Boots aus</string>
<string name="slot_a">Slot A</string>
<string name="slot_b">Slot B</string>
<string name="selected_slot">Selected slot: %1$s</string>
<string name="horizon_getting_original_slot">Getting the original slot</string>
<string name="horizon_setting_target_slot">Setting the specified slot</string>
<string name="horizon_restoring_original_slot">Restore Default Slot</string>
<string name="current_slot">Current Slot%1$s </string>
<string name="slot_b">Steckplatz B</string>
<string name="selected_slot">Wähle LKM: %1$s</string>
<string name="horizon_getting_original_slot">Den ursprünglichen Slot erhalten</string>
<string name="horizon_setting_target_slot">Setze den angegebenen Slot</string>
<string name="horizon_restoring_original_slot">Standard wiederherstellen</string>
<string name="current_slot">Aktueller Standard-Slot des Systems%1$s </string>
<!-- Error Messages -->
<string name="horizon_copy_failed">Copy failed</string>
<string name="horizon_unknown_error">Unknown error</string>
<string name="flash_failed_message">Flash failed</string>
<string name="horizon_copy_failed">Kopieren fehlgeschlagen</string>
<string name="horizon_unknown_error">Ein unbekannter Fehler ist aufgetreten</string>
<string name="flash_failed_message">Schreiben fehlgeschlagen</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="Lkm_install_methods">LKM Reparatur/Installation</string>
<string name="GKI_install_methods">Flashen des Kernels…</string>
<string name="kernel_version_log">Kernel</string>
<string name="tool_version_log">Benutze das Patchwerkzeug%1$s</string>
<string name="configuration">Konfigurieren</string>
<string name="app_settings">Anwendungs-Einstellungen</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
<string name="apps_with_custom_profile">Applications with customized configurations</string>
<string name="other_apps">Applications with unchanged defaults</string>
<string name="no_apps_found">Application not found</string>
<string name="selinux_enabled_toast">SELinux Enabled</string>
<string name="selinux_disabled_toast">SELinux Disabled</string>
<string name="selinux_change_failed">SELinux Status change failed</string>
<string name="advanced_settings">Advanced Settings</string>
<string name="appearance_settings">Customize the toolbar</string>
<string name="clear">Entfernen</string>
<string name="apps_with_root">Anwendungen mit Root-Rechten</string>
<string name="apps_with_custom_profile">Anwendungen mit angepassten Konfigurationen</string>
<string name="other_apps">Anwendungen mit unveränderten Standardeinstellungen</string>
<string name="no_apps_found">Anwendung nicht gefunden</string>
<string name="selinux_enabled_toast">SELinux aktiviert</string>
<string name="selinux_disabled_toast">SELinux deaktiviert</string>
<string name="selinux_change_failed">SELinux Statusänderung fehlgeschlagen</string>
<string name="advanced_settings">Erweiterte Einstellungen</string>
<string name="appearance_settings">Passt die Symbolleiste an.</string>
<string name="back">Comeback</string>
<string name="expand">Be in full swing</string>
<string name="collapse">put away</string>
<string name="susfs_enabled">SuSFS enabled</string>
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="root_require_for_install">Requires root privileges</string>
<string name="expand">Seien Sie in vollem Gange</string>
<string name="collapse">wegziehen</string>
<string name="susfs_enabled">SuSFS aktiviert</string>
<string name="susfs_disabled">SuSFS deaktiviert</string>
<string name="background_set_success">Hintergrund erfolgreich gesetzt</string>
<string name="background_removed">Eigene Hintergründe entfernt</string>
<string name="icon_switch_title">Alternatives Symbol</string>
<string name="icon_switch_summary">Ändere das Launcher-Symbol auf das KernelSU Icon.</string>
<string name="icon_switched">Icon gewechselt</string>
<string name="root_require_for_install">Erfordert Root-Rechte</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<string name="show_kpm_info">KPM-Funktion anzeigen</string>
<string name="show_kpm_info_summary">Versteckt KPM-Informationen und Funktion in der Home- und Unterleiste</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<string name="use_webuix">Wähle die zu verwendende WebUI-Engine</string>
<string name="engine_auto_select">Automatisch auswählen</string>
<string name="engine_force_webuix">Nutzung von WebUI X erzwingen</string>
<string name="engine_force_ksu">Pflichtanwendung von KSU WebUI</string>
<string name="use_webuix_eruda">Eruda in WebUI X injizieren</string>
<string name="use_webuix_eruda_summary">Fügen Sie eine Debug-Konsole in WebUI X ein, um das Debuggen zu vereinfachen. Benötigt Debugging im WebUI X.</string>
<!-- DPI setting related strings -->
<string name="dpi_settings">DPI setting</string>
<string name="app_dpi_title">Applied DPI</string>
<string name="app_dpi_summary">Adjust the screen display density for the current application only</string>
<string name="dpi_size_small">Small </string>
<string name="dpi_size_medium">Medium </string>
<string name="dpi_size_large">Big</string>
<string name="dpi_size_extra_large">oversize</string>
<string name="dpi_size_custom">customizable</string>
<string name="dpi_apply_settings">Applying DPI settings</string>
<string name="dpi_confirm_title">Confirm DPI change</string>
<string name="dpi_confirm_message">Are you sure you want to change the application DPI from %1$d to %2$d?</string>
<string name="dpi_confirm_summary">Application needs to be restarted to apply the new DPI settings, does not affect the system status bar or other applications</string>
<string name="dpi_applied_success">DPI has been set to %1$d, effective after restarting the application</string>
<string name="dpi_settings">DPI-Einstellung</string>
<string name="app_dpi_title">Angewendeter DPI</string>
<string name="app_dpi_summary">Bildschirmanzahl nur für die aktuelle Anwendung anpassen</string>
<string name="dpi_size_small">Klein </string>
<string name="dpi_size_medium">Mittel </string>
<string name="dpi_size_large">Groß</string>
<string name="dpi_size_extra_large">übergröße</string>
<string name="dpi_size_custom">anpassbar</string>
<string name="dpi_apply_settings">DPI-Einstellungen anwenden</string>
<string name="dpi_confirm_title">DPI-Änderung bestätigen</string>
<string name="dpi_confirm_message">Sind Sie sicher, dass Sie die Anwendung DPI von %1$d auf %2$d ändern möchten?</string>
<string name="dpi_confirm_summary">Die Anwendung muss neu gestartet werden, um die neuen DPI-Einstellungen zu übernehmen, hat keine Auswirkungen auf die System-Statusleiste oder andere Anwendungen</string>
<string name="dpi_applied_success">DPI wurde auf %1$dgesetzt, wirksam nach dem Neustart der Anwendung</string>
<!-- Language settings related strings -->
<string name="language_setting">App Language</string>
<string name="language_follow_system">Follow System</string>
<string name="language_changed">Language changed, restarting to apply changes</string>
<string name="settings_card_dim">Card Darkness Adjustment</string>
<string name="language_setting">App Sprache</string>
<string name="language_follow_system">Folge Systemeinstellung</string>
<string name="language_changed">Sprache geändert, Neustart um Änderungen zu übernehmen</string>
<string name="settings_card_dim">Kartenfinsternis Anpassung</string>
<!-- Super User Related -->
<string name="scroll_to_top">Top</string>
<string name="scroll_to_bottom">Bottom</string>
<string name="scroll_to_top_description">Scroll to top</string>
<string name="scroll_to_bottom_description">Scroll to the bottom</string>
<string name="authorized">authorized</string>
<string name="unauthorized">unauthorized</string>
<string name="selected">Selected</string>
<string name="select">option</string>
<string name="profile_umount_modules_disable">Disable custom uninstallation module</string>
<string name="scroll_to_bottom">Unten</string>
<string name="scroll_to_top_description">Bildlauf nach oben scrollen</string>
<string name="scroll_to_bottom_description">Scrolle zum Ende</string>
<string name="authorized">Autorisiert</string>
<string name="unauthorized">Unberechtigt</string>
<string name="selected">Ausgewählt</string>
<string name="select">variieren</string>
<string name="profile_umount_modules_disable">Eigenes Deinstallationsmodul deaktivieren</string>
<!-- Flash related -->
<string name="error_code">error code</string>
<string name="check_log">Please check the log</string>
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="error_code">fehlercode</string>
<string name="check_log">Bitte überprüfen Sie das Log</string>
<string name="installing_module">Modul wird installiert %1$d/%2$d</string>
<string name="module_failed_count">%d Fehler bei der Installation eines neuen Moduls</string>
<string name="module_download_error">Modul-Download fehlgeschlagen</string>
<string name="kernel_flashing">Kernel-Flashen</string>
</resources>

View File

@@ -11,7 +11,7 @@
<string name="home_unsupported_reason">KernelSU solo admite kernels GKI por ahora</string>
<string name="home_kernel">Versión del kernel</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">SuSFS Version</string>
<string name="home_susfs_version">Versión SuSFS</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_manager_version">Versión del gestor</string>
<string name="home_fingerprint">Huella del dispositivo</string>
@@ -25,10 +25,10 @@
<string name="module_failed_to_disable">Error al desactivar el módulo: %s</string>
<string name="module_empty">Ningún módulo instalado</string>
<string name="module">Módulo</string>
<string name="module_sort_action_first">Sort (Action first)</string>
<string name="module_sort_enabled_first">Sort (Enabled first)</string>
<string name="module_sort_action_first">Ordenar (Acción primero)</string>
<string name="module_sort_enabled_first">Ordenar (Activado primero)</string>
<string name="uninstall">Desinstalar</string>
<string name="restore">Restore</string>
<string name="restore">Restaurar</string>
<string name="module_install">Instalar</string>
<string name="install">Instalar</string>
<string name="reboot">Reiniciar</string>
@@ -56,7 +56,7 @@
<string name="home_click_to_learn_kernelsu">Aprende a instalar KernelSU y a utilizar módulos</string>
<string name="home_support_title">Apóyanos</string>
<string name="home_support_content">KernelSU es, y siempre será, gratuito y de código abierto. Sin embargo, puedes demostrarnos que te importamos haciendo una donación.</string>
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
<string name="about_source_code"><![CDATA[Ver el código fuente en %1$s<br/>Únete a nuestro canal %2$s]]></string>
<string name="profile_default">Predeterminado</string>
<string name="profile_template">Plantilla</string>
<string name="profile_custom">Personalizado</string>
@@ -70,10 +70,10 @@
<string name="profile_selinux_context">Contexto SELinux</string>
<string name="profile_umount_modules">Desmontar módulos</string>
<string name="failed_to_update_app_profile">Error al actualizar el perfil de la aplicación para %s</string>
<string name="require_kernel_version" formatted="false">The current KernelSU version %d is too low for the manager to work properly. Please upgrade to version %d or higher!</string>
<string name="require_kernel_version" formatted="false">La versión %d actual de KernelSU es demasiado baja para que el gestor funcione correctamente. Por favor, ¡actualice a la versión %d o superior!</string>
<string name="settings_umount_modules_default">Desmontar módulos por defecto</string>
<string name="settings_umount_modules_default_summary">El valor global predeterminado para \"Umount modules\" en App Profile. Si está activado, eliminará todas las modificaciones de módulos del sistema para las apps que no tengan un perfil establecido.</string>
<string name="settings_susfs_toggle">Disable kprobe hooks</string>
<string name="settings_susfs_toggle">Desactivar kprobe hooks</string>
<string name="profile_umount_modules_summary">Activar esta opción permitirá a KernelSU restaurar cualquier archivo modificado por los módulos para esta aplicación.</string>
<string name="profile_selinux_domain">Dominio</string>
<string name="profile_selinux_rules">Reglas</string>
@@ -111,8 +111,9 @@
<string name="settings_check_update">Comprobar actualización</string>
<string name="settings_check_update_summary">Comprobación automática de actualizaciones al abrir la aplicación</string>
<string name="grant_root_failed">¡No se ha podido conceder el acceso root!</string>
<string name="action">Action</string>
<string name="action">Aktion</string>
<string name="open">Abrir</string>
<string name="close">Cancelar</string>
<string name="enable_web_debugging">Activar la depuración de WebView</string>
<string name="enable_web_debugging_summary">Puede ser usado para depurar WebUI, por favor habilítalo sólo cuando sea necesario.</string>
<string name="direct_install">Instalación directa (Recomendada)</string>
@@ -129,240 +130,244 @@
<string name="settings_uninstall_temporary_message">Desinstalar temporalmente KernelSU, restaurar al estado original tras el siguiente reinicio.</string>
<string name="settings_uninstall_permanent_message">Desinstalar KernelSU (Root y todos los módulos) completa y permanentemente.</string>
<string name="settings_restore_stock_image_message">Restaurar la imagen de fábrica stock (Si existe una copia de seguridad), por lo general se utiliza antes de OTA; si necesita desinstalar KernelSU, por favor, utilice \"Desinstalar permanentemente\".</string>
<string name="flashing">Flashing</string>
<string name="flashing">Intermitencia</string>
<string name="flash_success">Éxito de Flash</string>
<string name="flash_failed">Flash falló</string>
<string name="selected_lkm">LKM seleccionado: %s</string>
<string name="save_log">Guardar registros</string>
<string name="log_saved">Logs saved</string>
<string name="status_supported">Supported</string>
<string name="status_not_supported">Not Supported</string>
<string name="status_unknown">Unknown</string>
<string name="sus_su_mode">SuS SU mode:</string>
<string name="log_saved">Registro guardado</string>
<string name="status_supported">Apóyanos</string>
<string name="status_not_supported">No soportado</string>
<string name="status_unknown">Desconocido</string>
<string name="sus_su_mode">Modo SuS SU:</string>
<!-- Module related -->
<string name="module_install_confirm">confirm install module %1$s</string>
<string name="unknown_module">unknown module</string>
<string name="module_install_confirm">¿confirmar la instalación del módulo %1$s?</string>
<string name="unknown_module">módulo desconocido</string>
<!-- Restore related -->
<string name="restore_confirm_title">Confirm Module Restoration</string>
<string name="restore_confirm_message">This operation will overwrite all existing modules. Continue?</string>
<string name="confirm">Confirm</string>
<string name="cancel">Cancel</string>
<string name="restore_confirm_title">Confirmar restauración del módulo</string>
<string name="restore_confirm_message">Esta operación sobrescribirá todos los módulos existentes. ¿Continuar?</string>
<string name="confirm">Confirmar</string>
<string name="cancel">Cancelar</string>
<!-- Backup related -->
<string name="backup_success">Backup successful (tar.gz)</string>
<string name="backup_failed">Backup failed: %1$s</string>
<string name="backup_modules">backup modules</string>
<string name="restore_modules">restore modules</string>
<string name="backup_success">Copia de seguridad exitosa (tar.gz)</string>
<string name="backup_failed">Copia de seguridad fallida: %1$s</string>
<string name="backup_modules">módulos de respaldo</string>
<string name="restore_modules">restaurar módulos</string>
<!-- Restore related messages -->
<string name="restore_success">Modules restored successfully, restart required</string>
<string name="restore_failed">Restore failed: %1$s</string>
<string name="restart_now">Restart Now</string>
<string name="unknown_error">Unknown error</string>
<string name="restore_success">Módulos restaurados con éxito, se requiere reiniciar</string>
<string name="restore_failed">Restauración fallida: %1$s</string>
<string name="restart_now">Reiniciar ahora</string>
<string name="unknown_error">Error desconocido</string>
<!-- Command related -->
<string name="command_execution_failed">Command execution failed: %1$s</string>
<string name="command_execution_failed">Ejecución del comando fallida: %1$s</string>
<!-- Allowlist related -->
<string name="allowlist_backup_success">Allowlist backup successful</string>
<string name="allowlist_backup_failed">Allowlist backup failed: %1$s</string>
<string name="allowlist_restore_confirm_title">Confirm Allowlist Restoration</string>
<string name="allowlist_restore_confirm_message">This operation will overwrite the current allowlist. Continue?</string>
<string name="allowlist_restore_success">Allowlist restored successfully</string>
<string name="allowlist_restore_failed">Allowlist restore failed: %1$s</string>
<string name="backup_allowlist">Backup Allowlist</string>
<string name="restore_allowlist">Restore Allowlist</string>
<string name="settings_custom_background">Custom App Background</string>
<string name="settings_custom_background_summary">Select an image as background</string>
<string name="settings_card_alpha">Navigation bar transparency</string>
<string name="settings_restore_default">Restore default</string>
<string name="home_android_version">Android version</string>
<string name="home_device_model">Device model</string>
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
<string name="settings_disable_su">Disable su compatibility</string>
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (existing root processes will not be affected).</string>
<string name="using_mksu_manager">You are using the SukiSU Beta manager</string>
<string name="module_install_multiple_confirm">Are you sure you want to install the selected %d modules?</string>
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
<string name="more_settings">More settings</string>
<string name="allowlist_backup_success">Copia de seguridad correcta</string>
<string name="allowlist_backup_failed">Copia de seguridad de lista fallida: %1$s</string>
<string name="allowlist_restore_confirm_title">Confirmar restauración de lista de permisos</string>
<string name="allowlist_restore_confirm_message">Esta operación sobrescribirá la lista permitida actual. ¿Continuar?</string>
<string name="allowlist_restore_success">Lista restaurada correctamente</string>
<string name="allowlist_restore_failed">Restauración de lista de permisos falló: %1$s</string>
<string name="backup_allowlist">Copia de seguridad lista</string>
<string name="restore_allowlist">Restaurar lista de permisos</string>
<string name="settings_custom_background">Fondo de aplicación personalizado</string>
<string name="settings_custom_background_summary">Seleccionar una imagen como fondo</string>
<string name="settings_card_alpha">Transparencia de la barra de navegación</string>
<string name="settings_restore_default">Restaurar</string>
<string name="home_android_version">Versión de Android</string>
<string name="home_device_model">Modelo del dispositivo</string>
<string name="su_not_allowed">No se permite conceder superusuario a %s</string>
<string name="settings_disable_su">Desactivar compatibilidad su</string>
<string name="settings_disable_su_summary">Deshabilita temporalmente cualquier aplicación para obtener privilegios de root a través del comando de \"it\" (los procesos de root existentes no se verán afectados).</string>
<string name="using_mksu_manager">Estás usando el administrador de la Beta de SukiSU</string>
<string name="module_install_multiple_confirm">¿Está seguro que desea instalar los módulos %d seleccionados?</string>
<string name="module_install_multiple_confirm_with_names">¿Seguro que quieres instalar los siguientes módulos %1$d ? \n\n%2$s</string>
<string name="more_settings">Opciones avanzadas</string>
<string name="selinux">SELinux</string>
<string name="selinux_enabled">Enabled</string>
<string name="selinux_disabled">Disabled</string>
<string name="simple_mode">Simplicity mode</string>
<string name="simple_mode_summary">Hides unnecessary cards when turned on</string>
<string name="hide_kernel_kernelsu_version">Hide kernel version</string>
<string name="hide_kernel_kernelsu_version_summary">Hide kernel version</string>
<string name="hide_other_info">Hide other info</string>
<string name="hide_other_info_summary">Hides information about the number of super users, modules and KPM modules on the home page</string>
<string name="hide_susfs_status">Hide SuSFS status</string>
<string name="hide_susfs_status_summary">Hide SuSFS status information on the home page</string>
<string name="hide_link_card">Hide Link Card Status</string>
<string name="hide_link_card_summary">Hide link card information on the home page</string>
<string name="theme_mode">Theme</string>
<string name="theme_follow_system">Follow system</string>
<string name="theme_light">Light</string>
<string name="theme_dark">Dark</string>
<string name="manual_hook">Manual Hook</string>
<string name="dynamic_color_title">Dynamic colours</string>
<string name="dynamic_color_summary">Dynamic colours using system themes</string>
<string name="choose_theme_color">Choose a theme colour</string>
<string name="color_default">Blue</string>
<string name="color_green">Green</string>
<string name="color_purple">Purple</string>
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="selinux_enabled">Habilitado</string>
<string name="selinux_disabled">Desactivado</string>
<string name="simple_mode">Modo de simplicidad</string>
<string name="simple_mode_summary">Ocultar tarjetas innecesarias al encender</string>
<string name="hide_kernel_kernelsu_version">Ocultar versión del núcleo</string>
<string name="hide_kernel_kernelsu_version_summary">Ocultar versión del núcleo</string>
<string name="hide_other_info">Ocultar otra información</string>
<string name="hide_other_info_summary">Oculta información sobre el número de superusuarios, módulos y módulos KPM en la página de inicio</string>
<string name="hide_susfs_status">Ocultar estado SuSFS</string>
<string name="hide_susfs_status_summary">Ocultar información de estado de SuSFS en la página de inicio</string>
<string name="hide_link_card">Ocultar el estado de la tarjeta de enlace</string>
<string name="hide_link_card_summary">Ocultar información de la tarjeta de enlace en la página de inicio</string>
<string name="theme_mode">Temas</string>
<string name="theme_follow_system">Predeterminado del sistema</string>
<string name="theme_light">Claro</string>
<string name="theme_dark">Oscuro</string>
<string name="manual_hook">Gancho manual</string>
<string name="dynamic_color_title">Color dinámico</string>
<string name="dynamic_color_summary">Colores dinámicos usando temas del sistema</string>
<string name="choose_theme_color">Elegir un color de tema</string>
<string name="color_default">Azul</string>
<string name="color_green">Verde</string>
<string name="color_purple">Morado</string>
<string name="color_orange">Naranjo</string>
<string name="color_pink">Rosa</string>
<string name="color_gray">Gris</string>
<string name="color_yellow">Amarillo</string>
<string name="flash_option">Opciones de flash</string>
<string name="flash_option_tip">Seleccione el archivo a flashear</string>
<string name="horizon_kernel">Install Anykernel3</string>
<string name="horizon_kernel_summary">Flash AnyKernel3 kernel file</string>
<string name="root_required">Requires root privileges</string>
<string name="copy_failed">File Copy Failure</string>
<string name="reboot_complete_title">Scrubbing complete</string>
<string name="reboot_complete_msg">Whether to reboot immediately</string>
<string name="yes">Yes</string>
<string name="horizon_kernel_summary">Flash archivo del kernel AnyKernel3</string>
<string name="root_required">Requiere privilegios de root</string>
<string name="copy_failed">Fallo al copiar archivo</string>
<string name="reboot_complete_title">Desguace completo</string>
<string name="reboot_complete_msg">¿Reiniciar inmediatamente?</string>
<string name="yes">Si</string>
<string name="no">No</string>
<string name="failed_reboot">Reboot Failed</string>
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="failed_reboot">Reinicio fallido</string>
<string name="batch_authorization">empoderar</string>
<string name="batch_cancel_authorization">gasto</string>
<string name="backup">Copia de seguridad</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">Version</string>
<string name="kpm_author">Author</string>
<string name="kpm_uninstall">Uninstall</string>
<string name="kpm_uninstall_success">Uninstalled successfully</string>
<string name="kpm_uninstall_failed">Failed to uninstall</string>
<string name="kpm_install">Install</string>
<string name="kpm_install_success">Load of kpm module successful</string>
<string name="kpm_install_failed">Load of kpm module failed</string>
<string name="kpm_args">Parameters</string>
<string name="kpm_control">Execute</string>
<string name="home_kpm_version">KPM Version</string>
<string name="close_notice">Close</string>
<string name="kernel_module_notice">The following kernel module functions were developed by KernelPatch and modified to include the kernel module functions of SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra Look forward to</string>
<string name="kpm_control_success">Success</string>
<string name="kpm_control_failed">Failed</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
<string name="custom_settings">Custom settings</string>
<string name="kpm_empty">No hay módulos del núcleo instalados en este momento</string>
<string name="kpm_version">Versión</string>
<string name="kpm_author">Autor</string>
<string name="kpm_uninstall">Desinstalar</string>
<string name="kpm_uninstall_success">Desinstalado con éxito</string>
<string name="kpm_uninstall_failed">Error al desinstalar</string>
<string name="kpm_install">Instalar</string>
<string name="kpm_install_success">Carga exitosa del módulo kpm</string>
<string name="kpm_install_failed">Error al cargar el módulo kpm</string>
<string name="kpm_args">Parámetros</string>
<string name="kpm_control">Empezar</string>
<string name="home_kpm_version">Versión de KPM</string>
<string name="close_notice">Cancelar</string>
<string name="kernel_module_notice">Las siguientes funciones del módulo del núcleo fueron desarrolladas por KernelPatch y modificadas para incluir las funciones del módulo del núcleo de SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra espera a</string>
<string name="kpm_control_success">Correctamente realizado</string>
<string name="kpm_control_failed">Fallido</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra será una rama relativamente independiente de KSU en el futuro, pero todavía apreciamos el KernelSU oficial y MKSU etc. ¡por sus contribuciones!</string>
<string name="not_supported">Sin soporte</string>
<string name="supported">Apoyado</string>
<string name="home_kpm_module">"Número de módulos KPM: %d "</string>
<string name="kpm_invalid_file">Archivo KPM inválido</string>
<string name="kernel_patched">Kernel no parcheado</string>
<string name="kernel_not_enabled">Kernel no configurado</string>
<string name="custom_settings">Ajustes personalizados</string>
<string name="kpm_install_mode">KPM Install</string>
<string name="kpm_install_mode_load">Load</string>
<string name="kpm_install_mode_embed">Embed</string>
<string name="kpm_install_mode_description">Please select: %1\$s Module Installation Mode \n\nLoad: Temporarily load the module \nEmbedded: Permanently install into the system</string>
<string name="log_failed_to_check_module_file">Failed to check module file existence</string>
<string name="snackbar_failed_to_check_module_file">Unable to check if module file exists</string>
<string name="confirm_uninstall_title">Confirm uninstallation</string>
<string name="confirm_uninstall_confirm">Uninstall</string>
<string name="confirm_uninstall_dismiss">Cancel</string>
<string name="theme_color">Theme Color</string>
<string name="invalid_file_type">Incorrect file type! Please select .kpm file.</string>
<string name="confirm_uninstall_title_with_filename">Uninstall</string>
<string name="confirm_uninstall_content">The following KPM will be uninstalled: %s</string>
<string name="settings_susfs_toggle_summary">Disable kprobe hooks created by KernelSU, using inline hooks instead, which is similar to non-GKI kernel hooking method.</string>
<string name="image_editor_title">Adjust background image</string>
<string name="image_editor_hint">Use two fingers to zoom the image, and one finger to drag it to adjust the position</string>
<string name="background_image_error">Could not load image</string>
<string name="reprovision">Reprovision</string>
<string name="kpm_install_mode_load">Cargar</string>
<string name="kpm_install_mode_embed">Insertar</string>
<string name="kpm_install_mode_description">Por favor seleccione: %1\$s Modo de instalación del Módulo \n\nCarga: Cargar temporalmente el módulo \nInsertar: Instalar permanentemente en el sistema</string>
<string name="log_failed_to_check_module_file">Error al comprobar la existencia del archivo de módulo</string>
<string name="snackbar_failed_to_check_module_file">No se puede comprobar si el archivo de módulo existe</string>
<string name="confirm_uninstall_title">Confirme las desinstalaciones</string>
<string name="confirm_uninstall_confirm">Desinstalar</string>
<string name="confirm_uninstall_dismiss">Cancelar</string>
<string name="theme_color">Color del tema</string>
<string name="invalid_file_type">¡Tipo de archivo incorrecto! Por favor seleccione el archivo .kpm.</string>
<string name="confirm_uninstall_title_with_filename">Desinstalar</string>
<string name="confirm_uninstall_content">El siguiente KPM será desinstalado: %s</string>
<string name="settings_susfs_toggle_summary">Deshabilita los ganchos kprobe creados por KernelSU, usando ganchos en línea en su lugar, que es similar al método de enganche del núcleo no GKI.</string>
<string name="image_editor_title">Ajustar imagen de fondo</string>
<string name="image_editor_hint">Usa dos dedos para acercar la imagen, y un dedo para arrastrarla para ajustar la posición</string>
<string name="background_image_error">Imposible cargar imagen</string>
<string name="reprovision">Reaprovisionamiento</string>
<!-- Kernel Flash Progress Related -->
<string name="horizon_flash_title">Kernel Flashing</string>
<string name="horizon_logs_label">Logs:</string>
<string name="horizon_flash_complete">Flash Complete</string>
<string name="horizon_flash_title">Parpadeo Kernel</string>
<string name="horizon_logs_label">Registros:</string>
<string name="horizon_flash_complete">Flashear completo</string>
<!-- Flash Status Related -->
<string name="horizon_preparing">Preparing</string>
<string name="horizon_cleaning_files">Cleaning files…</string>
<string name="horizon_copying_files">Copying files…</string>
<string name="horizon_extracting_tool">Extracting flash tool</string>
<string name="horizon_patching_script">Patching flash script…</string>
<string name="horizon_flashing">Flashing kernel…</string>
<string name="horizon_flash_complete_status">Flash completed</string>
<string name="horizon_preparing">Preparando</string>
<string name="horizon_cleaning_files">Limpiando archivos…</string>
<string name="horizon_copying_files">Copiando archivos…</string>
<string name="horizon_extracting_tool">Extrayendo herramienta flash…</string>
<string name="horizon_patching_script">Parcheando script flash</string>
<string name="horizon_flashing">Flashear kernel…</string>
<string name="horizon_flash_complete_status">Flash completado</string>
<!-- Slot selection related strings -->
<string name="select_slot_title">Select Flash Slot</string>
<string name="select_slot_description">Please select the target slot for flashing boot</string>
<string name="select_slot_title">Seleccionar Ranura Flash</string>
<string name="select_slot_description">Por favor, seleccione la ranura de destino para flashear el arranque</string>
<string name="slot_a">Slot A</string>
<string name="slot_b">Slot B</string>
<string name="selected_slot">Selected slot: %1$s</string>
<string name="horizon_getting_original_slot">Getting the original slot</string>
<string name="horizon_setting_target_slot">Setting the specified slot</string>
<string name="horizon_restoring_original_slot">Restore Default Slot</string>
<string name="current_slot">Current Slot%1$s </string>
<string name="selected_slot">Slot selectionada</string>
<string name="horizon_getting_original_slot">Obteniendo la ranura original</string>
<string name="horizon_setting_target_slot">Establecer la ranura especificada</string>
<string name="horizon_restoring_original_slot">Restaurar Ranura Predeterminada</string>
<string name="current_slot">Ranura predeterminada del sistema actual%1$s </string>
<!-- Error Messages -->
<string name="horizon_copy_failed">Copy failed</string>
<string name="horizon_unknown_error">Unknown error</string>
<string name="flash_failed_message">Flash failed</string>
<string name="horizon_copy_failed">Hubo un fallo al copiar</string>
<string name="horizon_unknown_error">Error desconocido</string>
<string name="flash_failed_message">Flash falló</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<string name="Lkm_install_methods">Reparación/instalación de LKM</string>
<string name="GKI_install_methods">Flashear kernel</string>
<string name="kernel_version_log">Versión del kernel</string>
<string name="tool_version_log">Usando la herramienta de parches%1$s</string>
<string name="configuration">Configurar</string>
<string name="app_settings">Configuración de la Aplicación</string>
<string name="tools">Herramientas</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
<string name="apps_with_custom_profile">Applications with customized configurations</string>
<string name="other_apps">Applications with unchanged defaults</string>
<string name="no_apps_found">Application not found</string>
<string name="selinux_enabled_toast">SELinux Enabled</string>
<string name="selinux_disabled_toast">SELinux Disabled</string>
<string name="selinux_change_failed">SELinux Status change failed</string>
<string name="advanced_settings">Advanced Settings</string>
<string name="appearance_settings">Customize the toolbar</string>
<string name="back">Comeback</string>
<string name="expand">Be in full swing</string>
<string name="collapse">put away</string>
<string name="susfs_enabled">SuSFS enabled</string>
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="root_require_for_install">Requires root privileges</string>
<string name="clear">Eliminaciones</string>
<string name="apps_with_root">Aplicaciones con privilegios de root</string>
<string name="apps_with_custom_profile">Aplicaciones con configuraciones personalizadas</string>
<string name="other_apps">Aplicaciones con valores por defecto sin cambios</string>
<string name="no_apps_found">No se ha encontrado la solicitud</string>
<string name="selinux_enabled_toast">SELinux habilitado</string>
<string name="selinux_disabled_toast">SELinux desactivado</string>
<string name="selinux_change_failed">Error al cambiar el estado de SELinux</string>
<string name="advanced_settings">Configuraciones avanzadas</string>
<string name="appearance_settings">Personalizar la barra de herramientas.</string>
<string name="back">Retorno</string>
<string name="expand">Estar en pleno swing</string>
<string name="collapse">poner</string>
<string name="susfs_enabled">SuSFS activado</string>
<string name="susfs_disabled">SuSFS desactivado</string>
<string name="background_set_success">Fondo establecido correctamente</string>
<string name="background_removed">Eliminar fondo personalizado</string>
<string name="icon_switch_title">Icono alternativo</string>
<string name="icon_switch_summary">Cambiar el icono del lanzador al icono de KernelSU.</string>
<string name="icon_switched">Icono cambiado</string>
<string name="root_require_for_install">Requiere privilegios de root</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<string name="show_kpm_info">Mostrar función KPM</string>
<string name="show_kpm_info_summary">Oculta la información y función del KPM en la barra de inicio e inferior</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<string name="use_webuix">Seleccione el motor WebUI a usar</string>
<string name="engine_auto_select">Selección automática</string>
<string name="engine_force_webuix">Forzar el uso de WebUI X</string>
<string name="engine_force_ksu">Uso obligatorio de KSU WebUI</string>
<string name="use_webuix_eruda">Inyectar Eruda en WebUI X</string>
<string name="use_webuix_eruda_summary">Inyecta una consola de depuración en WebUI X para facilitar la depuración. Requiere que la depuración web esté encendida.</string>
<!-- DPI setting related strings -->
<string name="dpi_settings">DPI setting</string>
<string name="app_dpi_title">Applied DPI</string>
<string name="app_dpi_summary">Adjust the screen display density for the current application only</string>
<string name="dpi_size_small">Small </string>
<string name="dpi_size_medium">Medium </string>
<string name="dpi_size_large">Big</string>
<string name="dpi_size_extra_large">oversize</string>
<string name="dpi_size_custom">customizable</string>
<string name="dpi_apply_settings">Applying DPI settings</string>
<string name="dpi_confirm_title">Confirm DPI change</string>
<string name="dpi_confirm_message">Are you sure you want to change the application DPI from %1$d to %2$d?</string>
<string name="dpi_confirm_summary">Application needs to be restarted to apply the new DPI settings, does not affect the system status bar or other applications</string>
<string name="dpi_applied_success">DPI has been set to %1$d, effective after restarting the application</string>
<string name="dpi_settings">Configuración DPI</string>
<string name="app_dpi_title">DPI aplicado</string>
<string name="app_dpi_summary">Ajustar la densidad de pantalla para la aplicación actual</string>
<string name="dpi_size_small">Pequeño </string>
<string name="dpi_size_medium">Medio </string>
<string name="dpi_size_large">Original</string>
<string name="dpi_size_extra_large">sobretamaño</string>
<string name="dpi_size_custom">personalizable</string>
<string name="dpi_apply_settings">Aplicando ajustes de DPI</string>
<string name="dpi_confirm_title">Confirmar cambio DPI</string>
<string name="dpi_confirm_message">¿Estás seguro de que quieres cambiar el DPI de la aplicación de %1$d a %2$d?</string>
<string name="dpi_confirm_summary">La aplicación necesita reiniciarse para aplicar la nueva configuración DPI, no afecta a la barra de estado del sistema u otras aplicaciones</string>
<string name="dpi_applied_success">DPI ha sido establecido a %1$d, efectivo después de reiniciar la aplicación</string>
<!-- Language settings related strings -->
<string name="language_setting">App Language</string>
<string name="language_follow_system">Follow System</string>
<string name="language_changed">Language changed, restarting to apply changes</string>
<string name="settings_card_dim">Card Darkness Adjustment</string>
<string name="language_setting">Idioma de la aplicación</string>
<string name="language_follow_system">Seguir sistema</string>
<string name="language_changed">Idioma cambiado, reiniciando para aplicar cambios</string>
<string name="settings_card_dim">Ajuste de oscuridad de tarjeta</string>
<!-- Super User Related -->
<string name="scroll_to_top">Top</string>
<string name="scroll_to_bottom">Bottom</string>
<string name="scroll_to_top_description">Scroll to top</string>
<string name="scroll_to_bottom_description">Scroll to the bottom</string>
<string name="authorized">authorized</string>
<string name="unauthorized">unauthorized</string>
<string name="selected">Selected</string>
<string name="select">option</string>
<string name="profile_umount_modules_disable">Disable custom uninstallation module</string>
<string name="scroll_to_top">Arriba</string>
<string name="scroll_to_bottom">Abajo</string>
<string name="scroll_to_top_description">Ir arriba</string>
<string name="scroll_to_bottom_description">Desplazar hacia abajo</string>
<string name="authorized">autorizado</string>
<string name="unauthorized">Sin Autorización</string>
<string name="selected">Seleccionados</string>
<string name="select">opción</string>
<string name="profile_umount_modules_disable">Desactivar módulo personalizado de desinstalación</string>
<!-- Flash related -->
<string name="error_code">error code</string>
<string name="check_log">Please check the log</string>
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="error_code">código de error</string>
<string name="check_log">Por favor, compruebe el registro</string>
<string name="installing_module">Módulo instalado %1$d/%2$d</string>
<string name="module_failed_count">%d falló al instalar un nuevo módulo</string>
<string name="module_download_error">La descarga del modelo falló</string>
<string name="kernel_flashing">Parpadeo Kernel</string>
</resources>

View File

@@ -113,6 +113,7 @@
<string name="grant_root_failed">Juurkasutaja andmine ebaõnnestus!</string>
<string name="action">Action</string>
<string name="open">Ava</string>
<string name="close">Close</string>
<string name="enable_web_debugging">Luba WebView silumine</string>
<string name="enable_web_debugging_summary">Saab kasutada WebUI silumiseks, palun luba ainult vajadusel.</string>
<string name="direct_install">Otsene paigaldus (soovitatud)</string>
@@ -208,7 +209,7 @@
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="color_yellow">Yellow</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="horizon_kernel">Install Anykernel3</string>
@@ -223,7 +224,6 @@
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">Version</string>
@@ -245,7 +245,7 @@
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="home_kpm_module">"Number of KPMs: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
@@ -296,13 +296,12 @@
<string name="flash_failed_message">Flash failed</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="GKI_install_methods">Flashing AnyKernel3</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
@@ -321,13 +320,18 @@
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="icon_switch_title">Alternate icon</string>
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon.</string>
<string name="icon_switched">Icon switched</string>
<string name="root_require_for_install">Requires root privileges</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix">Select the WebUI engine to use</string>
<string name="engine_auto_select">Automatic Selection</string>
<string name="engine_force_webuix">Force the use of WebUI X</string>
<string name="engine_force_ksu">Mandatory use of KSU WebUI</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<!-- DPI setting related strings -->
@@ -365,4 +369,5 @@
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="kernel_flashing">Kernel Flashing</string>
</resources>

View File

@@ -113,6 +113,7 @@
<string name="grant_root_failed">Failed to grant root!</string>
<string name="action">Action</string>
<string name="open">Open</string>
<string name="close">Close</string>
<string name="enable_web_debugging">Enable WebView debugging</string>
<string name="enable_web_debugging_summary">Can be used to debug WebUI. Please enable only when needed.</string>
<string name="direct_install">Direct install (Recommended)</string>
@@ -208,7 +209,7 @@
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="color_yellow">Yellow</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="horizon_kernel">Install Anykernel3</string>
@@ -223,7 +224,6 @@
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">Version</string>
@@ -245,7 +245,7 @@
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="home_kpm_module">"Number of KPMs: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
@@ -296,13 +296,12 @@
<string name="flash_failed_message">Flash failed</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="GKI_install_methods">Flashing AnyKernel3</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
@@ -321,13 +320,18 @@
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="icon_switch_title">Alternate icon</string>
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon.</string>
<string name="icon_switched">Icon switched</string>
<string name="root_require_for_install">Requires root privileges</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix">Select the WebUI engine to use</string>
<string name="engine_auto_select">Automatic Selection</string>
<string name="engine_force_webuix">Force the use of WebUI X</string>
<string name="engine_force_ksu">Mandatory use of KSU WebUI</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<!-- DPI setting related strings -->
@@ -365,4 +369,5 @@
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="kernel_flashing">Kernel Flashing</string>
</resources>

View File

@@ -113,6 +113,7 @@
<string name="grant_root_failed">Failed to grant root!</string>
<string name="action">Action</string>
<string name="open">Open</string>
<string name="close">Close</string>
<string name="enable_web_debugging">Enable WebView debugging</string>
<string name="enable_web_debugging_summary">Can be used to debug WebUI. Please enable only when needed.</string>
<string name="direct_install">Direct install (Recommended)</string>
@@ -208,7 +209,7 @@
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="color_yellow">Yellow</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="horizon_kernel">Install Anykernel3</string>
@@ -223,7 +224,6 @@
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">Version</string>
@@ -245,7 +245,7 @@
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="home_kpm_module">"Number of KPMs: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
@@ -296,13 +296,12 @@
<string name="flash_failed_message">Flash failed</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="GKI_install_methods">Flashing AnyKernel3</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
@@ -321,13 +320,18 @@
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="icon_switch_title">Alternate icon</string>
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon.</string>
<string name="icon_switched">Icon switched</string>
<string name="root_require_for_install">Requires root privileges</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix">Select the WebUI engine to use</string>
<string name="engine_auto_select">Automatic Selection</string>
<string name="engine_force_webuix">Force the use of WebUI X</string>
<string name="engine_force_ksu">Mandatory use of KSU WebUI</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<!-- DPI setting related strings -->
@@ -365,4 +369,5 @@
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="kernel_flashing">Kernel Flashing</string>
</resources>

View File

@@ -11,8 +11,8 @@
<string name="home_unsupported_reason">KernelSU ne prend désormais en charge que les noyaux GKI</string>
<string name="home_kernel">Noyau</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">SuSFS Version</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_susfs_version">Version SuSFS</string>
<string name="home_susfs_sus_su">SuS Su</string>
<string name="home_manager_version">Version du gestionnaire</string>
<string name="home_fingerprint">Empreinte digitale</string>
<string name="home_selinux_status">Mode SELinux</string>
@@ -28,7 +28,7 @@
<string name="module_sort_action_first">Trier par action</string>
<string name="module_sort_enabled_first">Trier par activé</string>
<string name="uninstall">Désinstaller</string>
<string name="restore">Restore</string>
<string name="restore">Restaure</string>
<string name="module_install">Installer</string>
<string name="install">Installer</string>
<string name="reboot">Redémarrer</string>
@@ -56,7 +56,7 @@
<string name="home_click_to_learn_kernelsu">Découvrez comment installer KernelSU et utiliser les modules</string>
<string name="home_support_title">Soutenez-nous</string>
<string name="home_support_content">KernelSU est, et restera toujours, gratuit et open source. Vous pouvez cependant nous témoigner de votre soutien en nous faisant un don.</string>
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
<string name="about_source_code"><![CDATA[Voir le code source à %1$s<br/>Rejoignez notre canal %2$s]]></string>
<string name="profile_default">Par défaut</string>
<string name="profile_template">Modèle</string>
<string name="profile_custom">Personnalisé</string>
@@ -70,10 +70,10 @@
<string name="profile_selinux_context">Contexte SELinux</string>
<string name="profile_umount_modules">Démonter les modules</string>
<string name="failed_to_update_app_profile">Échec de la modification du profil d\'application de %s</string>
<string name="require_kernel_version" formatted="false">The current KernelSU version %d is too low for the manager to work properly. Please upgrade to version %d or higher!</string>
<string name="require_kernel_version" formatted="false">La version actuelle de KernelSU (%d) est trop ancienne pour que le gestionnaire fonctionne correctement. Veuillez passer à la version %d ou à une version supérieure!</string>
<string name="settings_umount_modules_default">Démonter les modules par défaut</string>
<string name="settings_umount_modules_default_summary">Valeur globale par défaut pour l\'option \"Démonter les modules\" dans les profils d\'application. Lorsque l\'option est activée, les modifications apportées au système par les modules sont supprimées pour les applications qui n\'ont pas de profil défini.</string>
<string name="settings_susfs_toggle">Disable kprobe hooks</string>
<string name="settings_susfs_toggle">Désactiver les crochets kprobe</string>
<string name="profile_umount_modules_summary">L\'activation de cette option permettra à KernelSU de restaurer tous les fichiers modifiés par les modules pour cette application.</string>
<string name="profile_selinux_domain">Domaine</string>
<string name="profile_selinux_rules">Règles</string>
@@ -113,6 +113,7 @@
<string name="grant_root_failed">Échec de l\'octroi des privilèges root!</string>
<string name="action">Action</string>
<string name="open">Ouvrir</string>
<string name="close">Fermer</string>
<string name="enable_web_debugging">Activer le débogage WebView</string>
<string name="enable_web_debugging_summary">Peut être utilisé pour déboguer WebUI. Activez uniquement cette option si nécessaire.</string>
<string name="direct_install">Installation directe (recommandé)</string>
@@ -137,234 +138,238 @@
<string name="selected_lkm">LKM sélectionné: %s</string>
<string name="save_log">Enregistrer les journaux</string>
<string name="log_saved">Journaux enregistrés</string>
<string name="status_supported">Supported</string>
<string name="status_not_supported">Not Supported</string>
<string name="status_unknown">Unknown</string>
<string name="sus_su_mode">SuS SU mode:</string>
<string name="status_supported">Supporté</string>
<string name="status_not_supported">Non supporté</string>
<string name="status_unknown">Inconnu</string>
<string name="sus_su_mode">Mode Sus</string>
<!-- Module related -->
<string name="module_install_confirm">confirm install module %1$s</string>
<string name="unknown_module">unknown module</string>
<string name="module_install_confirm">confirmer l\'installation du module %1$s?</string>
<string name="unknown_module">module inconnu</string>
<!-- Restore related -->
<string name="restore_confirm_title">Confirm Module Restoration</string>
<string name="restore_confirm_message">This operation will overwrite all existing modules. Continue?</string>
<string name="confirm">Confirm</string>
<string name="cancel">Cancel</string>
<string name="restore_confirm_title">Confirmer la restauration</string>
<string name="restore_confirm_message">Cette opération va écraser les modules existants. Continuer?</string>
<string name="confirm">Confirmer</string>
<string name="cancel">Annuler</string>
<!-- Backup related -->
<string name="backup_success">Backup successful (tar.gz)</string>
<string name="backup_failed">Backup failed: %1$s</string>
<string name="backup_modules">backup modules</string>
<string name="restore_modules">restore modules</string>
<string name="backup_success">Sauvegarde réussie (tar.gz)</string>
<string name="backup_failed">Échec de la sauvegarde : %1$s</string>
<string name="backup_modules">modules de sauvegarde</string>
<string name="restore_modules">Restaurer les modules</string>
<!-- Restore related messages -->
<string name="restore_success">Modules restored successfully, restart required</string>
<string name="restore_failed">Restore failed: %1$s</string>
<string name="restart_now">Restart Now</string>
<string name="unknown_error">Unknown error</string>
<string name="restore_success">Succès de la sauvegarde, redémarrer</string>
<string name="restore_failed">Échec de la restauration : %1$s</string>
<string name="restart_now">Redémarrer</string>
<string name="unknown_error">Erreur inconnue</string>
<!-- Command related -->
<string name="command_execution_failed">Command execution failed: %1$s</string>
<string name="command_execution_failed">L\'exécution de la commande a échoué : %1$s</string>
<!-- Allowlist related -->
<string name="allowlist_backup_success">Allowlist backup successful</string>
<string name="allowlist_backup_failed">Allowlist backup failed: %1$s</string>
<string name="allowlist_restore_confirm_title">Confirm Allowlist Restoration</string>
<string name="allowlist_restore_confirm_message">This operation will overwrite the current allowlist. Continue?</string>
<string name="allowlist_restore_success">Allowlist restored successfully</string>
<string name="allowlist_restore_failed">Allowlist restore failed: %1$s</string>
<string name="backup_allowlist">Backup Allowlist</string>
<string name="restore_allowlist">Restore Allowlist</string>
<string name="settings_custom_background">Custom App Background</string>
<string name="settings_custom_background_summary">Select an image as background</string>
<string name="settings_card_alpha">Navigation bar transparency</string>
<string name="settings_restore_default">Restore default</string>
<string name="home_android_version">Android version</string>
<string name="home_device_model">Device model</string>
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
<string name="settings_disable_su">Disable su compatibility</string>
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (existing root processes will not be affected).</string>
<string name="using_mksu_manager">You are using the SukiSU Beta manager</string>
<string name="module_install_multiple_confirm">Are you sure you want to install the selected %d modules?</string>
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
<string name="more_settings">More settings</string>
<string name="allowlist_backup_success">Sauvegarde de la liste blanche réussie</string>
<string name="allowlist_backup_failed">La sauvegarde de la liste d\'autorisations a échoué : %1$s</string>
<string name="allowlist_restore_confirm_title">Confirmer la restauration de la liste blanche</string>
<string name="allowlist_restore_confirm_message">Cette opération écrasera la liste blanche actuelle. Continuer ?</string>
<string name="allowlist_restore_success">Liste blanche restaurée avec succès</string>
<string name="allowlist_restore_failed">La restauration de la liste d\'autorisations a échoué : %1$s</string>
<string name="backup_allowlist">Sauvegarder la liste blanche</string>
<string name="restore_allowlist">Restaurer la liste blanche</string>
<string name="settings_custom_background">Arrière-plan personnalisé de l\'application</string>
<string name="settings_custom_background_summary">Image as arrière-plan</string>
<string name="settings_card_alpha">Transparence de la barre de navigation</string>
<string name="settings_restore_default">Restaurer par défaut</string>
<string name="home_android_version">Version Android</string>
<string name="home_device_model">Modèle du téléphone</string>
<string name="su_not_allowed">Donner un super-utilisateur à %s n\'est pas autorisé</string>
<string name="settings_disable_su">Désactiver la compatibilité su</string>
<string name="settings_disable_su_summary">Désactiver temporairement l\'accès des applications aux privilèges root via la commande su (les processus root existants ne seront pas affectés).</string>
<string name="using_mksu_manager">Vous utilisez le gestionnaire SukiSU Beta</string>
<string name="module_install_multiple_confirm">Êtes-vous sûr de vouloir installer les modules %d sélectionnés ?</string>
<string name="module_install_multiple_confirm_with_names">Êtes-vous sûr de vouloir installer les modules %1$d suivants ? \n\n%2$s</string>
<string name="more_settings">Autres configurations</string>
<string name="selinux">SELinux</string>
<string name="selinux_enabled">Enabled</string>
<string name="selinux_disabled">Disabled</string>
<string name="simple_mode">Simplicity mode</string>
<string name="simple_mode_summary">Hides unnecessary cards when turned on</string>
<string name="hide_kernel_kernelsu_version">Hide kernel version</string>
<string name="hide_kernel_kernelsu_version_summary">Hide kernel version</string>
<string name="hide_other_info">Hide other info</string>
<string name="hide_other_info_summary">Hides information about the number of super users, modules and KPM modules on the home page</string>
<string name="hide_susfs_status">Hide SuSFS status</string>
<string name="hide_susfs_status_summary">Hide SuSFS status information on the home page</string>
<string name="hide_link_card">Hide Link Card Status</string>
<string name="hide_link_card_summary">Hide link card information on the home page</string>
<string name="theme_mode">Theme</string>
<string name="theme_follow_system">Follow system</string>
<string name="theme_light">Light</string>
<string name="theme_dark">Dark</string>
<string name="manual_hook">Manual Hook</string>
<string name="dynamic_color_title">Dynamic colours</string>
<string name="dynamic_color_summary">Dynamic colours using system themes</string>
<string name="choose_theme_color">Choose a theme colour</string>
<string name="color_default">Blue</string>
<string name="color_green">Green</string>
<string name="color_purple">Purple</string>
<string name="selinux_enabled">Activé</string>
<string name="selinux_disabled">Désactivé</string>
<string name="simple_mode">Me simple</string>
<string name="simple_mode_summary">Masque les cartes inutiles lorsqu\'il est activé</string>
<string name="hide_kernel_kernelsu_version">Masquer la version du noyau</string>
<string name="hide_kernel_kernelsu_version_summary">Masquer la version du noyau</string>
<string name="hide_other_info">Masquer les autres infos</string>
<string name="hide_other_info_summary">Masque des informations sur le nombre de super utilisateurs, de modules et de modules KPM sur la page d\'accueil</string>
<string name="hide_susfs_status">Masquer le statut SuSFS</string>
<string name="hide_susfs_status_summary">Masquer les informations de la carte de lien sur la page d\'accueil</string>
<string name="hide_link_card">Masquer le statut du lien de la carte</string>
<string name="hide_link_card_summary">Masquer les informations de la carte de lien sur la page d\'accueil</string>
<string name="theme_mode">Thème</string>
<string name="theme_follow_system">Suivre le système</string>
<string name="theme_light">Clair</string>
<string name="theme_dark">Sombre</string>
<string name="manual_hook">Crochet manuel</string>
<string name="dynamic_color_title">Couleur dynamique</string>
<string name="dynamic_color_summary">Couleurs dynamiques en utilisant des thèmes système</string>
<string name="choose_theme_color">Choisir une couleur de thème</string>
<string name="color_default">Bleu</string>
<string name="color_green">Vert</string>
<string name="color_purple">Violet</string>
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="color_pink">Rose</string>
<string name="color_gray">Gris</string>
<string name="color_yellow">Jaune</string>
<string name="flash_option">Options du flash</string>
<string name="flash_option_tip">Sélectionnez le fichier à installer</string>
<string name="horizon_kernel">Install Anykernel3</string>
<string name="horizon_kernel_summary">Flash AnyKernel3 kernel file</string>
<string name="root_required">Requires root privileges</string>
<string name="copy_failed">File Copy Failure</string>
<string name="reboot_complete_title">Scrubbing complete</string>
<string name="reboot_complete_msg">Whether to reboot immediately</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="failed_reboot">Reboot Failed</string>
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="horizon_kernel_summary">Fichier noyau AnyKernel3</string>
<string name="root_required">Nécessite les privilèges root</string>
<string name="copy_failed">Échec de la copie du fichier</string>
<string name="reboot_complete_title">Traitement terminé</string>
<string name="reboot_complete_msg">Redémarrer immédiatement ?</string>
<string name="yes">Oui</string>
<string name="no">Non</string>
<string name="failed_reboot">Échec du redémarrage</string>
<string name="batch_authorization">ePouvoir</string>
<string name="batch_cancel_authorization">retirer</string>
<string name="backup">Sauvegarder</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_empty">Aucun module de noyau installé pour le moment</string>
<string name="kpm_version">Version</string>
<string name="kpm_author">Author</string>
<string name="kpm_uninstall">Uninstall</string>
<string name="kpm_uninstall_success">Uninstalled successfully</string>
<string name="kpm_uninstall_failed">Failed to uninstall</string>
<string name="kpm_install">Install</string>
<string name="kpm_install_success">Load of kpm module successful</string>
<string name="kpm_install_failed">Load of kpm module failed</string>
<string name="kpm_args">Parameters</string>
<string name="kpm_control">Execute</string>
<string name="home_kpm_version">KPM Version</string>
<string name="close_notice">Close</string>
<string name="kernel_module_notice">The following kernel module functions were developed by KernelPatch and modified to include the kernel module functions of SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra Look forward to</string>
<string name="kpm_control_success">Success</string>
<string name="kpm_control_failed">Failed</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
<string name="custom_settings">Custom settings</string>
<string name="kpm_install_mode">KPM Install</string>
<string name="kpm_install_mode_load">Load</string>
<string name="kpm_install_mode_embed">Embed</string>
<string name="kpm_install_mode_description">Please select: %1\$s Module Installation Mode \n\nLoad: Temporarily load the module \nEmbedded: Permanently install into the system</string>
<string name="log_failed_to_check_module_file">Failed to check module file existence</string>
<string name="snackbar_failed_to_check_module_file">Unable to check if module file exists</string>
<string name="confirm_uninstall_title">Confirm uninstallation</string>
<string name="confirm_uninstall_confirm">Uninstall</string>
<string name="confirm_uninstall_dismiss">Cancel</string>
<string name="theme_color">Theme Color</string>
<string name="invalid_file_type">Incorrect file type! Please select .kpm file.</string>
<string name="confirm_uninstall_title_with_filename">Uninstall</string>
<string name="confirm_uninstall_content">The following KPM will be uninstalled: %s</string>
<string name="settings_susfs_toggle_summary">Disable kprobe hooks created by KernelSU, using inline hooks instead, which is similar to non-GKI kernel hooking method.</string>
<string name="image_editor_title">Adjust background image</string>
<string name="image_editor_hint">Use two fingers to zoom the image, and one finger to drag it to adjust the position</string>
<string name="background_image_error">Could not load image</string>
<string name="reprovision">Reprovision</string>
<string name="kpm_author">Auteur</string>
<string name="kpm_uninstall">Désinstaller</string>
<string name="kpm_uninstall_success">Désinstallé avec succès</string>
<string name="kpm_uninstall_failed">Échec de la désinstallation : </string>
<string name="kpm_install">Installer</string>
<string name="kpm_install_success">Chargement du module kpm réussi</string>
<string name="kpm_install_failed">Le chargement du module kpm a échoué</string>
<string name="kpm_args">Paramètres</string>
<string name="kpm_control">Exécuter</string>
<string name="home_kpm_version">Version de KPM</string>
<string name="close_notice">Fermer</string>
<string name="kernel_module_notice">Les fonctions suivantes du module du noyau ont été développées par KernelPatch et modifiées pour inclure les fonctions du module du noyau de SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra attend avec impatience</string>
<string name="kpm_control_success">Succès</string>
<string name="kpm_control_failed">Echoué</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra sera une branche relativement indépendante de KSU dans le futur, mais nous apprécions toujours le KernelSU officiel, MKSU etc. pour leurs contributions!</string>
<string name="not_supported">Non pris en charge</string>
<string name="supported">Pris en charge</string>
<string name="home_kpm_module">"Nombre de modules KPM : %d "</string>
<string name="kpm_invalid_file">Fichier KPM invalide</string>
<string name="kernel_patched">Noyau non corrigé</string>
<string name="kernel_not_enabled">Noyau non configuré</string>
<string name="custom_settings">Paramètres personnalisés</string>
<string name="kpm_install_mode">KPM Installé</string>
<string name="kpm_install_mode_load">Charger</string>
<string name="kpm_install_mode_embed">Intégrer</string>
<string name="kpm_install_mode_description">Veuillez sélectionner : %1\$s Mode d\'installation du module \n\nCharge : Chargez temporairement le module \nIntégré: Installez définitivement dans le système</string>
<string name="log_failed_to_check_module_file">Impossible de vérifier l\'existence du fichier du module</string>
<string name="snackbar_failed_to_check_module_file">Impossible de vérifier si le fichier du module existe</string>
<string name="confirm_uninstall_title">Confirmer la désinstallation.</string>
<string name="confirm_uninstall_confirm">Désinstaller</string>
<string name="confirm_uninstall_dismiss">Annuler</string>
<string name="theme_color">Couleur du thème</string>
<string name="invalid_file_type">Type de fichier incorrect ! Veuillez sélectionner un fichier .kpm.</string>
<string name="confirm_uninstall_title_with_filename">Désinstaller</string>
<string name="confirm_uninstall_content">Le KPM suivant sera désinstallé : %s</string>
<string name="settings_susfs_toggle_summary">Désactivez les crochets kprobe créés par KernelSU, en utilisant des crochets en ligne à la place, ce qui est similaire à la méthode de crochet du noyau non-GKI.</string>
<string name="image_editor_title">Ajuster l\'image de fond</string>
<string name="image_editor_hint">Utilisez deux doigts pour zoomer l\'image, et un doigt pour le faire glisser pour ajuster la position</string>
<string name="background_image_error">Impossible de charger l\'image</string>
<string name="reprovision">Remise à disposition</string>
<!-- Kernel Flash Progress Related -->
<string name="horizon_flash_title">Kernel Flashing</string>
<string name="horizon_logs_label">Logs:</string>
<string name="horizon_flash_complete">Flash Complete</string>
<string name="horizon_flash_title">Clignotement du noyau</string>
<string name="horizon_logs_label">Journaux:</string>
<string name="horizon_flash_complete">Flash terminé</string>
<!-- Flash Status Related -->
<string name="horizon_preparing">Preparing</string>
<string name="horizon_cleaning_files">Cleaning files…</string>
<string name="horizon_copying_files">Copying files…</string>
<string name="horizon_extracting_tool">Extracting flash tool</string>
<string name="horizon_patching_script">Patching flash script…</string>
<string name="horizon_flashing">Flashing kernel</string>
<string name="horizon_flash_complete_status">Flash completed</string>
<string name="horizon_preparing">Préparation de</string>
<string name="horizon_cleaning_files">Nettoyage des fichiers…</string>
<string name="horizon_copying_files">Copie des fichiers…</string>
<string name="horizon_extracting_tool">Extraction de l\'outil flash…</string>
<string name="horizon_patching_script">Mise à jour du script…</string>
<string name="horizon_flashing">Flash du noyau</string>
<string name="horizon_flash_complete_status">Flash complété</string>
<!-- Slot selection related strings -->
<string name="select_slot_title">Select Flash Slot</string>
<string name="select_slot_description">Please select the target slot for flashing boot</string>
<string name="select_slot_title">Sélectionnez l\'emplacement de Flash</string>
<string name="select_slot_description">Veuillez sélectionner l\'emplacement cible pour le démarrage du flash</string>
<string name="slot_a">Slot A</string>
<string name="slot_b">Slot B</string>
<string name="selected_slot">Selected slot: %1$s</string>
<string name="horizon_getting_original_slot">Getting the original slot</string>
<string name="horizon_setting_target_slot">Setting the specified slot</string>
<string name="horizon_restoring_original_slot">Restore Default Slot</string>
<string name="current_slot">Current Slot%1$s </string>
<string name="selected_slot">LKM sélectionné: %1$s</string>
<string name="horizon_getting_original_slot">Obtention de l\'emplacement original</string>
<string name="horizon_setting_target_slot">Définition de l\'emplacement spécifié</string>
<string name="horizon_restoring_original_slot">Restaurer l\'emplacement par défaut</string>
<string name="current_slot">Emplacement actuel</string>
<!-- Error Messages -->
<string name="horizon_copy_failed">Copy failed</string>
<string name="horizon_unknown_error">Unknown error</string>
<string name="flash_failed_message">Flash failed</string>
<string name="horizon_copy_failed">Copie échouée</string>
<string name="horizon_unknown_error">Erreur inconnue</string>
<string name="flash_failed_message">Échec du flash</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<string name="Lkm_install_methods">Réparation/installation LKM</string>
<string name="GKI_install_methods">Flash du noyau…</string>
<string name="kernel_version_log">Version du noyau%1$s</string>
<string name="tool_version_log">Utilisation de l\'outil de correctifs%1$s</string>
<string name="configuration">Configurer</string>
<string name="app_settings">Paramètres de l\'application</string>
<string name="tools">Outils</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
<string name="apps_with_custom_profile">Applications with customized configurations</string>
<string name="other_apps">Applications with unchanged defaults</string>
<string name="no_apps_found">Application not found</string>
<string name="selinux_enabled_toast">SELinux Enabled</string>
<string name="selinux_disabled_toast">SELinux Disabled</string>
<string name="selinux_change_failed">SELinux Status change failed</string>
<string name="advanced_settings">Advanced Settings</string>
<string name="appearance_settings">Customize the toolbar</string>
<string name="back">Comeback</string>
<string name="expand">Be in full swing</string>
<string name="collapse">put away</string>
<string name="susfs_enabled">SuSFS enabled</string>
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="root_require_for_install">Requires root privileges</string>
<string name="clear">Suppressions</string>
<string name="apps_with_root">Applications avec droits root</string>
<string name="apps_with_custom_profile">Applications avec configurations personnalisées</string>
<string name="other_apps">Applications avec valeurs par défaut non modifiées</string>
<string name="no_apps_found">Application introuvable</string>
<string name="selinux_enabled_toast">SELinux activé</string>
<string name="selinux_disabled_toast">SELinux désactivé</string>
<string name="selinux_change_failed">La modification du statut SELinux a échoué</string>
<string name="advanced_settings">Paramètres avancés</string>
<string name="appearance_settings">Choisir les boutons à afficher</string>
<string name="back">Reviens</string>
<string name="expand">Etre en plein swing</string>
<string name="collapse">rangé</string>
<string name="susfs_enabled">SuSFS activé</string>
<string name="susfs_disabled">SuSFS désactivé</string>
<string name="background_set_success">Fond d\'écran défini avec succès</string>
<string name="background_removed">Fond d\'écran personnalisé supprimé</string>
<string name="icon_switch_title">Icône alternative</string>
<string name="icon_switch_summary">Changer l\'icône du lanceur en icône de KernelSU.</string>
<string name="icon_switched">Icône changée</string>
<string name="root_require_for_install">Nécessite les privilèges root</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<string name="show_kpm_info">Afficher la fonction KPM</string>
<string name="show_kpm_info_summary">Masque les informations et fonctions KPM dans la barre d\'accueil et en bas</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<string name="use_webuix">Sélectionnez le moteur WebUI à utiliser</string>
<string name="engine_auto_select">Sélectionner automatiquement</string>
<string name="engine_force_webuix">Forcer l\'utilisation de WebUI X</string>
<string name="engine_force_ksu">Utilisation obligatoire de KSU WebUI</string>
<string name="use_webuix_eruda">Injecter Eruda dans WebUI X</string>
<string name="use_webuix_eruda_summary">Injectez une console de débogage dans WebUI X pour faciliter le débogage. Nécessite que le débogage soit activé.</string>
<!-- DPI setting related strings -->
<string name="dpi_settings">DPI setting</string>
<string name="app_dpi_title">Applied DPI</string>
<string name="app_dpi_summary">Adjust the screen display density for the current application only</string>
<string name="dpi_size_small">Small </string>
<string name="dpi_size_medium">Medium </string>
<string name="dpi_size_large">Big</string>
<string name="dpi_size_extra_large">oversize</string>
<string name="dpi_size_custom">customizable</string>
<string name="dpi_apply_settings">Applying DPI settings</string>
<string name="dpi_confirm_title">Confirm DPI change</string>
<string name="dpi_confirm_message">Are you sure you want to change the application DPI from %1$d to %2$d?</string>
<string name="dpi_confirm_summary">Application needs to be restarted to apply the new DPI settings, does not affect the system status bar or other applications</string>
<string name="dpi_applied_success">DPI has been set to %1$d, effective after restarting the application</string>
<string name="dpi_settings">Réglage du DPI</string>
<string name="app_dpi_title">DPI appliqué</string>
<string name="app_dpi_summary">Ajuster la densité d\'affichage de l\'écran pour l\'application actuelle uniquement</string>
<string name="dpi_size_small">Petit </string>
<string name="dpi_size_medium">Moyenne </string>
<string name="dpi_size_large">Grand</string>
<string name="dpi_size_extra_large">surtaille</string>
<string name="dpi_size_custom">Personnalisable</string>
<string name="dpi_apply_settings">Application des paramètres DPI</string>
<string name="dpi_confirm_title">Confirmer le changement de DPI</string>
<string name="dpi_confirm_message">Êtes-vous sûr de vouloir changer le DPI de l\'application de %1$d à %2$d?</string>
<string name="dpi_confirm_summary">L\'application doit être redémarrée pour appliquer les nouveaux paramètres de DPI, n\'affecte pas la barre d\'état du système ou d\'autres applications</string>
<string name="dpi_applied_success">Le DPI a été réglé sur %1$d, effectif après le redémarrage de l\'application</string>
<!-- Language settings related strings -->
<string name="language_setting">App Language</string>
<string name="language_follow_system">Follow System</string>
<string name="language_changed">Language changed, restarting to apply changes</string>
<string name="settings_card_dim">Card Darkness Adjustment</string>
<string name="language_setting">Langue de l\'application</string>
<string name="language_follow_system">Suivre le paramètre système</string>
<string name="language_changed">Langue modifiée, redémarrage pour appliquer les modifications</string>
<string name="settings_card_dim">Ajustement de l\'obscurité de la carte</string>
<!-- Super User Related -->
<string name="scroll_to_top">Top</string>
<string name="scroll_to_bottom">Bottom</string>
<string name="scroll_to_top_description">Scroll to top</string>
<string name="scroll_to_bottom_description">Scroll to the bottom</string>
<string name="authorized">authorized</string>
<string name="unauthorized">unauthorized</string>
<string name="selected">Selected</string>
<string name="scroll_to_top">En haut</string>
<string name="scroll_to_bottom">En Bas</string>
<string name="scroll_to_top_description">Aller en haut</string>
<string name="scroll_to_bottom_description">Faire défiler vers le bas</string>
<string name="authorized">Autori</string>
<string name="unauthorized">non autori</string>
<string name="selected">Sélectionné</string>
<string name="select">option</string>
<string name="profile_umount_modules_disable">Disable custom uninstallation module</string>
<string name="profile_umount_modules_disable">Désactiver le module de désinstallation personnalisé</string>
<!-- Flash related -->
<string name="error_code">error code</string>
<string name="check_log">Please check the log</string>
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="error_code">code d\'erreur</string>
<string name="check_log">Veuillez vérifier le journal</string>
<string name="installing_module">Module en cours d\'installation %1$d/%2$d</string>
<string name="module_failed_count">%d a échoué à installer un nouveau module</string>
<string name="module_download_error">Le téléchargement du modèle a échoué</string>
<string name="kernel_flashing">Clignotement du noyau</string>
</resources>

View File

@@ -113,6 +113,7 @@
<string name="grant_root_failed">Failed to grant root!</string>
<string name="action">Action</string>
<string name="open">Open</string>
<string name="close">Close</string>
<string name="enable_web_debugging">Enable WebView debugging</string>
<string name="enable_web_debugging_summary">Can be used to debug WebUI. Please enable only when needed.</string>
<string name="direct_install">Direct install (Recommended)</string>
@@ -208,7 +209,7 @@
<string name="color_orange">Orange</string>
<string name="color_pink">Pink</string>
<string name="color_gray">Gray</string>
<string name="color_ivory">Ivory</string>
<string name="color_yellow">Yellow</string>
<string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string>
<string name="horizon_kernel">Install Anykernel3</string>
@@ -223,7 +224,6 @@
<string name="batch_authorization">empower</string>
<string name="batch_cancel_authorization">withdraw</string>
<string name="backup">Backup</string>
<string name="color_yellow">Yellow</string>
<string name="kpm_title">KPM</string>
<string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">Version</string>
@@ -245,7 +245,7 @@
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but we still appreciate the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">Unsupported</string>
<string name="supported">Supported</string>
<string name="home_kpm_module">"Number of KPM modules: %d "</string>
<string name="home_kpm_module">"Number of KPMs: %d "</string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
@@ -296,13 +296,12 @@
<string name="flash_failed_message">Flash failed</string>
<!-- lkm/gki install -->
<string name="Lkm_install_methods">LKM repair/installation</string>
<string name="GKI_install_methods">GKI/non-GKI installation</string>
<string name="GKI_install_methods">Flashing AnyKernel3</string>
<string name="kernel_version_log">Kernel version%1$s</string>
<string name="tool_version_log">Using the patching tool%1$s</string>
<string name="configuration">Configure</string>
<string name="app_settings">Application Settings</string>
<string name="tools">Tools</string>
<string name="currently_selected">Currently</string>
<!-- String resources used in SuperUser -->
<string name="clear">Removals</string>
<string name="apps_with_root">Applications with root privileges</string>
@@ -321,13 +320,18 @@
<string name="susfs_disabled">SuSFS disabled</string>
<string name="background_set_success">Background set successfully</string>
<string name="background_removed">Removed custom backgrounds</string>
<string name="icon_switch_title">Alternate icon</string>
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon.</string>
<string name="icon_switched">Icon switched</string>
<string name="root_require_for_install">Requires root privileges</string>
<!-- KPM display settings -->
<string name="show_kpm_info">Display KPM Function</string>
<string name="show_kpm_info_summary">Display KPM information and Function in home and bottom bar (Need to reopen the app)</string>
<!-- Webui X settings -->
<string name="use_webuix">Use WebUI X</string>
<string name="use_webuix_summary">Use WebUI X instead of WebUI which supports more API\'s</string>
<string name="use_webuix">Select the WebUI engine to use</string>
<string name="engine_auto_select">Automatic Selection</string>
<string name="engine_force_webuix">Force the use of WebUI X</string>
<string name="engine_force_ksu">Mandatory use of KSU WebUI</string>
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
<!-- DPI setting related strings -->
@@ -365,4 +369,5 @@
<string name="installing_module">Module being installed %1$d/%2$d</string>
<string name="module_failed_count">%d Failed to install a new module</string>
<string name="module_download_error">Module download failed</string>
<string name="kernel_flashing">Kernel Flashing</string>
</resources>

Some files were not shown because too many files have changed in this diff Show More