/assets/cover.png
-```
diff --git a/docs/README_PT.md b/docs/README_PT.md
deleted file mode 100644
index 569c155b..00000000
--- a/docs/README_PT.md
+++ /dev/null
@@ -1,81 +0,0 @@
-[English](README.md) || **Português (Brasil)** || [Azərbaycan](README_az.md)
-
-
-
-
Seu gerenciador de módulos altamente personalizável
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- FAQ •
- ModConf •
- ModFS •
- Installer
-
-
-# Sobre
-
-Apresentando Magisk Module Repo Loader (MMRL) - O gerenciador de módulos definitivo para Magisk, KernelSU e APatch no Android. Este aplicativo altamente configurável permite aos usuários gerenciar módulos sem esforço, ao mesmo tempo que é completamente livre de anúncios.
-
-# Requisitos
-
-- Android 8.0 ou Superior
-- [MMRL-CLI](https://github.com/DerGoogler/MMRL-CLI)
-- 4-5 GB RAM (Menor pode ser possível)
-
-
-# Características
-
-- Basico: Literalmente básico
-- [ModFS](https://github.com/DerGoogler/MMRL/tree/master/docs/ModFS.md): Sistema de arquivos de módulo personalizável
-- [ModConf](https://github.com/DerGoogler/MMRL/blob/master/docs/ModConf/README.md): Fornece páginas criadas dinâmicas para módulos
-- Repos personalizados: carregue qualquer repositório que use o MRepo ou o GR Fork
-
-> Leia a [documentação](https://github.com/DerGoogler/MMRL/tree/master/docs) para explorar mais de nossas funções como `Shell`, `SuFile` e muito mais
-
-### Gerenciadores root compatíveis
-
-- [x] [Magisk](https://github.com/topjohnwu/Magisk)
-- [x] [Magisk Delta](https://github.com/HuskyDG/magisk-files)
-- [x] [KernelSU](https://github.com/tiann/KernelSU)
-- [x] [APatch](https://github.com/bmax121/APatch)
-
-# Capturas de Tela
-
-
-
-
-
-
-
-
-
-
-
-
-# Créditos e Agradecimentos
-
-- [tabler/tabler-icons](https://github.com/tabler/tabler-icons.git)
-- [Googlers-Repo/node-native](https://github.com/Googlers-Repo/node-native)
-- [topjohnwu/libsu](https://github.com/topjohnwu/libsu)
-- [Fox2Code/FoxMagiskModuleManager](https://github.com/Fox2Code/FoxMagiskModuleManager)
-- [DerGoogler/dgm-cms](https://github.com/DerGoogler/dgm-cms)
-- [Hentai-Web/Core](https://github.com/Hentai-Web/Core)
-- [Hentai-Web/Android](https://github.com/Hentai-Web/Android)
diff --git a/docs/README_az.md b/docs/README_az.md
deleted file mode 100644
index 5cc039a9..00000000
--- a/docs/README_az.md
+++ /dev/null
@@ -1,81 +0,0 @@
-[English](README.md) || [Português](README_PT.md) || **Azərbaycan**
-
-
-
-
Son dərəcə fərdiləşdirilə bilən modul meneceriniz
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- TSS •
- ModConf •
- ModFS •
- Installer
-
-
-# Xülasə
-
-Magisk Module Repo Loader (MMRL) ilə tanış olun - Android-də Magisk, KernelSU ve APatch üçün son modul meneceri. Bu son derəcə konfiqurasiya edilə bilən proqram istifadəçilərə modulları asanlıqla idarə etməyə imkan verir, eyni zamanda reklamlardan tamamilə azaddır.
-
-# Tələblər
-
-- Android 8.0 və ya sonrakı versiyalar
-- [MMRL-CLI](https://github.com/DerGoogler/MMRL-CLI)
-- 4-5 GB RAM (daha aşağı mümkün ola bilər)
-
-
-# Xüsusiyyətləri
-
-- Əsas biliklər: Sözün əsl mənasında əsas biliklər
-- [ModFS](https://github.com/DerGoogler/MMRL/tree/master/docs/ModFS.md): Fərdiləşdirilə Bilən Modul Fayl Sistemi
-- [ModConf](https://github.com/DerGoogler/MMRL/blob/master/docs/ModConf/README.md): Modullar üçün dinamik yaradılmış səhifələr təmin edin
-- Xüsusi Anbarlar: MRepo və ya GR Fork istifadə edən istənilən anbarı yükləyin
-
-> Shell, SuFile və daha çoxu kimi daxa çox funksiyalarımızı kəşf etmək üçün [sənədləri](https://github.com/DerGoogler/MMRL/tree/master/docs) oxuyun
-
-### Dəstəklənən kök meneceri
-
-- [x] [Magisk](https://github.com/topjohnwu/Magisk)
-- [x] [Magisk Delta](https://github.com/HuskyDG/magisk-files)
-- [x] [KernelSU](https://github.com/tiann/KernelSU)
-- [x] [APatch](https://github.com/bmax121/APatch)
-
-# Ekran görüntüləri
-
-
-
-
-
-
-
-
-
-
-
-
-# Kredit və Təşəkkürlər
-
-- [tabler/tabler-icons](https://github.com/tabler/tabler-icons.git)
-- [Googlers-Repo/node-native](https://github.com/Googlers-Repo/node-native)
-- [topjohnwu/libsu](https://github.com/topjohnwu/libsu)
-- [Fox2Code/FoxMagiskModuleManager](https://github.com/Fox2Code/FoxMagiskModuleManager)
-- [DerGoogler/dgm-cms](https://github.com/DerGoogler/dgm-cms)
-- [Hentai-Web/Core](https://github.com/Hentai-Web/Core)
-- [Hentai-Web/Android](https://github.com/Hentai-Web/Android)
diff --git a/docs/faq.md b/docs/faq.md
deleted file mode 100644
index 83058bd8..00000000
--- a/docs/faq.md
+++ /dev/null
@@ -1,22 +0,0 @@
-[googleplay-release]: https://play.google.com/store/apps/details?id=com.dergoogler.mmrl
-[github-release]: https://github.com/DerGoogler/MMRL
-[mmrlini]: https://github.com/DerGoogler/mmrl_install_tools
-[mmrlini-release]: https://github.com/DerGoogler/mmrl_install_tools/releases
-
-# **F**requently **A**sked **Q**uestions
-
-> We do not recommend to delete the entire `/data/adb/mmrl` folder. This folder contains your saved ModConf Playground file and other config files like from [MMRL Install Tools][mmrlini-release]
-
-## MMRL does not load correctly or shows a empty screen
-
-This my due internal config changes or invalid config files
-
-To solve this, try the following options:
-
-- reinstall the app from [GitHub Releases][github-release] or the [Google Play Store][googleplay-release]
-- clean app data and cache
-- removing `/data/adb/mmrl/settings.v*.json` or `/data/adb/mmrl`
-
-## Cannot install or update modules
-
-Try updating [MMRL Install Tools][mmrlini-release] or remove the `/data/adb/mmrl/mmrlini.v*.ini` config file
diff --git a/docs/faq_az.md b/docs/faq_az.md
deleted file mode 100644
index d5191191..00000000
--- a/docs/faq_az.md
+++ /dev/null
@@ -1,22 +0,0 @@
-[googleplay-release]: https://play.google.com/store/apps/details?id=com.dergoogler.mmrl
-[github-release]: https://github.com/DerGoogler/MMRL
-[mmrlini]: https://github.com/DerGoogler/mmrl_install_tools
-[mmrlini-release]: https://github.com/DerGoogler/mmrl_install_tools/releases
-
-# **T**ez-Tez **S**oruşulan **S**uallar
-
-> Biz bütün `/data/adb/mmrl` qovluğunu silməyi tövsiyə etmirik. Bu qovluğa saxladığınız ModConf OyunMeydanı faylı və [MMRL Quraşdırma Alətləri][mmrlini-release] kimi digər konfiqurasiya faylları ehtiva edir.
-
-## MMRL düzgün şəkildə yüklənmir və ya boş bir ekran göstərir
-
-Bu, mənim daxili konfiqurasiya dəyişikliklərim və ya etibarsız konfiqurasiya fayllarıma görədir
-
-Bunu həll etmək üçün aşağıdakı seçimləri sınayın:
-
-- proqramı [GitHub Releases][github-release] və ya [Google Play Market][googleplay-release] ünvanından yenidən quraşdırın
-- proqram məlumatını və keşi təmizləyin
-- `/data/adb/mmrl/settings.v*.json` və ya `/data/adb/mmrl` silin
-
-## Modullar yüklənə və ya yenilənə bilmir
-
-[MMRL Quraşdırma Alətlərini][mmrlini-release] yeniləməyə cəhd edin və ya `/data/adb/mmrl/mmrlini.v*.ini` konfiqurasiya faylını silin
diff --git a/fastlane/metadata/android/ar/full_description.txt b/fastlane/metadata/android/ar/full_description.txt
new file mode 100644
index 00000000..5db6bcf0
--- /dev/null
+++ b/fastlane/metadata/android/ar/full_description.txt
@@ -0,0 +1,9 @@
+MMRL هو تطبيق Android يساعد في إدارة مستودع الإضافت النمطية الخاص بك.
+
+ المميزات:
+ - إدارة مستودع الإضافات الخاصة بك
+ - دعم مستودعات متعددة
+ - دعم Magisk وKernelSU
+ - إنشاء Jetpack وتصميم Material 3
+
+ https://github.com/DerGoogler/MMRL
diff --git a/fastlane/metadata/android/ar/short_description.txt b/fastlane/metadata/android/ar/short_description.txt
new file mode 100644
index 00000000..f3992635
--- /dev/null
+++ b/fastlane/metadata/android/ar/short_description.txt
@@ -0,0 +1 @@
+مدير إضافات لـMagisk وKernelSU
diff --git a/fastlane/metadata/android/ar/title.txt b/fastlane/metadata/android/ar/title.txt
new file mode 100644
index 00000000..7cfd0c2a
--- /dev/null
+++ b/fastlane/metadata/android/ar/title.txt
@@ -0,0 +1 @@
+MMRL
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt
index 1fcc4754..e50f7e30 100644
--- a/fastlane/metadata/android/en-US/full_description.txt
+++ b/fastlane/metadata/android/en-US/full_description.txt
@@ -1,23 +1,9 @@
-MMRL is a highly configurable app allows you to manage modules effortlessly, all while being completely free of ads.
+MMRL is an Android app that helps manage your own modules repository.
+Features:
+- Manage your modules repository
+- Support multiple repositories
+- Support Magisk and KernelSU
+- Jetpack Compose & Material Design 3
-What root managers support MMRL?
- ✅ Magisk
- ✅ KernelSU
- ✅ APatch
-
-
-Which features does MMRL offer?
- 🫂 User friendly UI
- ⚙️ Configurable module pages (ModConf)
- 🗂️ Customizing all paths (such as "/data/adb/modules", if your root uses an other path)
- ✏️ ModConf Playground
- 📚 Viewing installed and updatable modules
- 📈 Adding covers, screenshots, verification, dependencies and more (repo based)
- 🔍 Search functionality
- 📄 Adding up to 5 repositories
-
-Requirements:
-- A rooted Android phone
-- A installed WebView engine
-- MMRL-CLI to use the full functionality
\ No newline at end of file
+https://github.com/DerGoogler/MMRL
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png
index 074aab12..e29b65f7 100644
Binary files a/fastlane/metadata/android/en-US/images/icon.png and b/fastlane/metadata/android/en-US/images/icon.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
index 78476fd9..2f8fabec 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
index bfad7d22..867f0260 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
index 884d3072..0bbe348b 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
index c755e8d9..d8736c09 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
index 3497baf7..53e83fa4 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png
index 9b932635..0c8d7f00 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png
deleted file mode 100644
index f3916239..00000000
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png and /dev/null differ
diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt
index 6af6f856..7ed72ef2 100644
--- a/fastlane/metadata/android/en-US/short_description.txt
+++ b/fastlane/metadata/android/en-US/short_description.txt
@@ -1 +1 @@
-Introducing MMRL - the ultimate manager for Magisk, KernelSU, and APatch.
\ No newline at end of file
+A modules manager for Magisk & KernelSU
diff --git a/fastlane/metadata/android/en-US/title.txt b/fastlane/metadata/android/en-US/title.txt
new file mode 100644
index 00000000..4ccb1641
--- /dev/null
+++ b/fastlane/metadata/android/en-US/title.txt
@@ -0,0 +1 @@
+MMRL
\ No newline at end of file
diff --git a/fastlane/metadata/android/ru-RU/full_description.txt b/fastlane/metadata/android/ru-RU/full_description.txt
new file mode 100644
index 00000000..dcb31c87
--- /dev/null
+++ b/fastlane/metadata/android/ru-RU/full_description.txt
@@ -0,0 +1,9 @@
+Это приложение помогает управлять вашим собственным репозиторием модулей.
+
+Возможности:
+- Управление репозиторием модулей
+- Поддержка нескольких репозиториев
+- Поддержка Magisk и KernelSU
+- JetPack Compose и Материальный Дизайн 3
+
+https://github.com/mrepoapp/mrepo
diff --git a/fastlane/metadata/android/ru-RU/short_description.txt b/fastlane/metadata/android/ru-RU/short_description.txt
new file mode 100644
index 00000000..9ff5f092
--- /dev/null
+++ b/fastlane/metadata/android/ru-RU/short_description.txt
@@ -0,0 +1 @@
+Менеджер модулей для Magisk и KernelSU
diff --git a/fastlane/metadata/android/ru-RU/title.txt b/fastlane/metadata/android/ru-RU/title.txt
new file mode 100644
index 00000000..7cfd0c2a
--- /dev/null
+++ b/fastlane/metadata/android/ru-RU/title.txt
@@ -0,0 +1 @@
+MMRL
diff --git a/gradle.properties b/gradle.properties
index 81cf2daa..e0dbbdbe 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,22 +1,9 @@
-# Project-wide Gradle settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+org.gradle.caching=true
+org.gradle.configuration-cache=true
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
android.useAndroidX=true
-android.enableJetifier=true
-org.gradle.daemon=false
-android.suppressUnsupportedCompileSdk=34
+android.nonTransitiveRClass=true
+
+kapt.include.compile.classpath=false
+kotlin.code.style=official
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 00000000..f0f63b2c
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,105 @@
+[versions]
+androidGradlePlugin = "8.4.1"
+androidxActivity = "1.9.0"
+androidxAnnotation = "1.8.0"
+androidxAppCompat = "1.6.1"
+androidxCompose = "1.7.0-beta01"
+androidxComposeMaterial3 = "1.2.1"
+androidxCore = "1.13.1"
+androidxCoreSplashscreen = "1.0.1"
+androidxDataStore = "1.1.1"
+androidxDocumentFile = "1.0.1"
+androidxHiltNavigationCompose = "1.2.0"
+androidxLifecycle = "2.8.0"
+androidxNavigation = "2.7.7"
+androidxRoom = "2.6.1"
+coilCompose = "2.1.0"
+hiddenApiRefine = "4.4.0"
+hilt = "2.51.1"
+kotlin = "2.0.0"
+kotlinReflect = "1.9.24"
+kotlinxCoroutines = "1.8.1"
+kotlinxDatetime = "0.6.0"
+ksp = "2.0.0-1.0.21"
+libsu = "5.2.2"
+protobuf = "4.27.0"
+protobufPlugin = "0.9.4"
+semver = "2.0.0"
+shizuku = "13.1.5"
+squareRetrofit = "2.11.0"
+squareOkhttp = "4.12.0"
+squareMoshi = "1.15.1"
+
+[libraries]
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" }
+androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidxAnnotation" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" }
+androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidxComposeMaterial3" }
+androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "androidxCompose" }
+androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "androidxCompose" }
+androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "androidxCompose" }
+androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util", version.ref = "androidxCompose" }
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" }
+androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" }
+androidx-datastore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" }
+androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "androidxDocumentFile" }
+androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" }
+androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidxLifecycle" }
+androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" }
+androidx-lifecycle-service = { group = "androidx.lifecycle", name = "lifecycle-service", version.ref = "androidxLifecycle" }
+androidx-lifecycle-viewModel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }
+androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" }
+androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "androidxRoom" }
+androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "androidxRoom" }
+androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "androidxRoom" }
+coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
+hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
+hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
+kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinReflect" }
+kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
+kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" }
+libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" }
+libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" }
+protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" }
+protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" }
+rikka-refine-annotation = { module = "dev.rikka.tools.refine:annotation", version.ref = "hiddenApiRefine" }
+rikka-refine-compiler = { module = "dev.rikka.tools.refine:annotation-processor", version.ref = "hiddenApiRefine" }
+rikka-refine-runtime = { module = "dev.rikka.tools.refine:runtime", version.ref = "hiddenApiRefine" }
+rikka-shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" }
+rikka-shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku" }
+semver = { module = "io.github.z4kn4fein:semver", version.ref = "semver" }
+square-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "squareRetrofit" }
+square-retrofit-moshi = { group = "com.squareup.retrofit2", name = "converter-moshi", version.ref = "squareRetrofit" }
+square-okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "squareOkhttp" }
+square-okhttp-dnsoverhttps = { group = "com.squareup.okhttp3", name = "okhttp-dnsoverhttps", version.ref = "squareOkhttp" }
+square-logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "squareOkhttp" }
+square-moshi = { group = "com.squareup.moshi", name = "moshi", version.ref = "squareMoshi" }
+square-moshi-kotlin = { group = "com.squareup.moshi", name = "moshi-kotlin-codegen", version.ref = "squareMoshi" }
+
+markwon-core = "io.noties.markwon:core:4.6.2"
+hiddenApiBypass = "org.lsposed.hiddenapibypass:hiddenapibypass:4.3"
+timber = "com.jakewharton.timber:timber:5.0.1"
+
+# Dependencies of the included build-logic
+android-gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
+compose-gradle= { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" }
+kotlin-gradle = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
+ksp-gradle = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
+android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
+compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
+kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
+kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
+ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+rikka-refine = { id = "dev.rikka.tools.refine", version.ref = "hiddenApiRefine" }
+protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" }
+
+# Plugins defined by this project
+self-application = { id = "self.application", version = "unspecified" }
+self-library = { id = "self.library", version = "unspecified" }
+self-compose = { id = "self.compose", version = "unspecified" }
+self-hilt = { id = "self.hilt", version = "unspecified" }
+self-room = { id = "self.room", version = "unspecified" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 8c0fb64a..d64cd491 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 59968b76..a000404b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Aug 15 17:04:39 CEST 2024
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
+#Tue Sep 24 18:52:42 CEST 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 91a7e269..1aa94a42 100644
--- a/gradlew
+++ b/gradlew
@@ -1,79 +1,127 @@
-#!/usr/bin/env bash
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
-warn ( ) {
+warn () {
echo "$*"
-}
+} >&2
-die ( ) {
+die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -82,83 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=$((i+1))
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index 8a0b282a..7101f8e4 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,90 +1,92 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/hidden-api/build.gradle.kts b/hidden-api/build.gradle.kts
new file mode 100644
index 00000000..a0286637
--- /dev/null
+++ b/hidden-api/build.gradle.kts
@@ -0,0 +1,13 @@
+plugins {
+ alias(libs.plugins.self.library)
+}
+
+android {
+ namespace = "com.dergoogler.mmrl.hidden_api"
+}
+
+dependencies {
+ annotationProcessor(libs.rikka.refine.compiler)
+ compileOnly(libs.rikka.refine.annotation)
+ compileOnly(libs.androidx.annotation)
+}
\ No newline at end of file
diff --git a/hidden-api/src/main/java/android/app/ActivityThread.java b/hidden-api/src/main/java/android/app/ActivityThread.java
new file mode 100644
index 00000000..c1b50074
--- /dev/null
+++ b/hidden-api/src/main/java/android/app/ActivityThread.java
@@ -0,0 +1,7 @@
+package android.app;
+
+public class ActivityThread {
+ public static Application currentApplication() {
+ throw new RuntimeException("Stub!");
+ }
+}
\ No newline at end of file
diff --git a/hidden-api/src/main/java/android/os/IPowerManager.java b/hidden-api/src/main/java/android/os/IPowerManager.java
new file mode 100644
index 00000000..e6f23a29
--- /dev/null
+++ b/hidden-api/src/main/java/android/os/IPowerManager.java
@@ -0,0 +1,12 @@
+package android.os;
+
+public interface IPowerManager extends IInterface {
+ void reboot(boolean confirm, String reason, boolean wait) throws RemoteException;
+
+ abstract class Stub extends Binder implements IPowerManager {
+
+ public static IPowerManager asInterface(IBinder binder) {
+ throw new RuntimeException("Stub!");
+ }
+ }
+}
diff --git a/hidden-api/src/main/java/android/os/PowerManagerHidden.java b/hidden-api/src/main/java/android/os/PowerManagerHidden.java
new file mode 100644
index 00000000..6b8ec379
--- /dev/null
+++ b/hidden-api/src/main/java/android/os/PowerManagerHidden.java
@@ -0,0 +1,13 @@
+package android.os;
+
+import androidx.annotation.RequiresApi;
+
+import dev.rikka.tools.refine.RefineAs;
+
+@RefineAs(PowerManager.class)
+public class PowerManagerHidden {
+ @RequiresApi(30)
+ public static boolean isRebootingUserspaceSupportedImpl() {
+ throw new RuntimeException("Stub!");
+ }
+}
diff --git a/hidden-api/src/main/java/android/os/SELinux.java b/hidden-api/src/main/java/android/os/SELinux.java
new file mode 100644
index 00000000..79c66bff
--- /dev/null
+++ b/hidden-api/src/main/java/android/os/SELinux.java
@@ -0,0 +1,6 @@
+package android.os;
+
+public class SELinux {
+
+ public static native String getContext();
+}
\ No newline at end of file
diff --git a/hidden-api/src/main/java/android/os/ServiceManager.java b/hidden-api/src/main/java/android/os/ServiceManager.java
new file mode 100644
index 00000000..6718fb30
--- /dev/null
+++ b/hidden-api/src/main/java/android/os/ServiceManager.java
@@ -0,0 +1,7 @@
+package android.os;
+
+public class ServiceManager {
+ public static IBinder getService(String name) {
+ throw new RuntimeException("Stub!");
+ }
+}
\ No newline at end of file
diff --git a/licensefix.js b/licensefix.js
deleted file mode 100644
index 38ce641a..00000000
--- a/licensefix.js
+++ /dev/null
@@ -1,40 +0,0 @@
-const fs = require("fs");
-const path = require("path");
-const package = require(path.resolve(__dirname, "package.json"));
-
-const NODE_MODULES_PATH = path.resolve(__dirname, "node_modules");
-const OUTPUT = path.resolve(__dirname, "src", "util", "licenses.json");
-const allDependencies = [];
-
-Object.keys(package.dependencies).forEach((dependency) => {
- const depPackage = require(path.resolve(__dirname, NODE_MODULES_PATH, dependency, "package.json"));
-
- if (depPackage) {
- function getSource() {
- if (depPackage.repository) {
- return depPackage.repository.url ? depPackage.repository.url : null;
- } else {
- return null;
- }
- }
-
- function getAuthor() {
- if (depPackage.author) {
- return depPackage.author.name ? depPackage.author.name : null;
- } else {
- return null;
- }
- }
-
- allDependencies.push({
- name: depPackage.name,
- author: getAuthor(),
- license: depPackage.license,
- description: depPackage.description,
- version: depPackage.version,
- source: `https://www.npmjs.com/package/${depPackage.name}`,
- });
- }
-});
-
-fs.writeFileSync(OUTPUT, JSON.stringify(allDependencies, null, 2));
diff --git a/package.json b/package.json
deleted file mode 100644
index 50a1a64f..00000000
--- a/package.json
+++ /dev/null
@@ -1,171 +0,0 @@
-{
- "name": "com.dergoogler.mmrl.web",
- "description": "",
- "config": {
- "cname": "mmrl.dergoogler.com",
- "application_id": "com.dergoogler.mmrl",
- "min_sdk": 26,
- "target_sdk": 34,
- "version_name": "3.24.33",
- "version_code": 32433,
- "verified_hosts": [
- [
- "mmrl",
- "i"
- ],
- [
- "localhost",
- "i"
- ],
- [
- "mmrl.dergoogler.com",
- "i"
- ],
- [
- "dergoogler.com",
- "i"
- ],
- [
- "dergoogler.github.io",
- "i"
- ],
- [
- "gr.dergoogler.com",
- "i"
- ],
- [
- "googlers-repo.github.io",
- "i"
- ],
- [
- "(localhost|\\b(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)(?::\\d{0,4})?\\b)",
- "g"
- ]
- ]
- },
- "main": "index.tsx",
- "keywords": [],
- "author": "Der_Googler",
- "license": "GPL-3.0",
- "scripts": {
- "start:dev": "webpack-dev-server --open --config webpack.dev.ts",
- "start:prod": "npm run licensefix && webpack-dev-server --open --config webpack.prod.ts",
- "web:dev": "webpack --config webpack.dev.ts",
- "web:prod": "npm run licensefix && webpack --config webpack.prod.ts",
- "web:prod-app": "npm run web:prod",
- "licensefix": "node licensefix.js",
- "deploy": "node deploy.js"
- },
- "resolutions": {
- "react": "^18.2.0",
- "react-dom": "^18.1.0",
- "@types/webpack": "^5.28.0"
- },
- "dependencies": {
- "@babel/runtime": "^7.23.2",
- "@babel/standalone": "^7.24.0",
- "@emotion/react": "^11.9.0",
- "@emotion/styled": "^11.8.1",
- "@giscus/react": "^2.4.0",
- "@monaco-editor/react": "^4.6.0",
- "@mui/icons-material": "^5.16.5",
- "@mui/lab": "^5.0.0-alpha.160",
- "@mui/material": "^5.15.2",
- "@nyariv/sandboxjs": "^0.8.23",
- "@primer/octicons-react": "^19.9.0",
- "@zenfs/core": "^0.17.1",
- "@zenfs/dom": "^0.2.15",
- "ajv": "^8.12.0",
- "anser": "^2.1.1",
- "axios": "^1.6.2",
- "default-composer": "^0.6.0",
- "eruda": "^3.0.0",
- "escape-carriage": "^1.3.1",
- "flatlist-react": "^1.5.14",
- "googlers-tools": "^1.2.8",
- "highlight.js": "^11.6.0",
- "ini": "^4.1.1",
- "linkify-it": "^5.0.0",
- "localforage": "^1.10.0",
- "markdown-to-jsx": "^7.4.0",
- "material-icons": "^1.10.8",
- "material-ui-confirm": "^3.0.16",
- "modfs": "^1.4.2",
- "monaco-editor": "^0.48.0",
- "monaco-editor-core": "^0.50.0",
- "monaco-languageclient": "^6.5.0",
- "object-assign": "^4.1.1",
- "onsenui": "^2.12.8",
- "properties-file": "^3.2.10",
- "react": "^18.2.0",
- "react-device-detect": "^2.2.3",
- "react-disappear": "^1.1.3",
- "react-dom": "^18.2.0",
- "react-fast-marquee": "^1.6.1",
- "react-onsenui": "^1.13.2",
- "react-render-tools": "^1.0.1",
- "react-syntax-highlighter": "^15.5.0",
- "react-transition-group": "^4.4.5",
- "react-zoom-pan-pinch": "^3.3.0",
- "reflect-metadata": "^0.2.2",
- "semver": "^7.6.3",
- "underscore": "^1.13.6",
- "usehooks-ts": "^3.1.0",
- "uuid": "^10.0.0",
- "yaml": "^2.3.4"
- },
- "devDependencies": {
- "@babel/core": "^7.24.0",
- "@babel/preset-env": "^7.23.6",
- "@babel/preset-react": "^7.23.3",
- "@octokit/rest": "^21.0.1",
- "@types/babel__core": "^7.20.2",
- "@types/babel__standalone": "^7.1.7",
- "@types/fs-extra": "^11.0.4",
- "@types/gh-pages": "^6.1.0",
- "@types/ini": "^4.1.0",
- "@types/node": "^18.19.50",
- "@types/object-assign": "^4.0.30",
- "@types/react": "^18.2.67",
- "@types/react-dom": "^18.0.2",
- "@types/react-onsenui": "^2.9.17",
- "@types/react-syntax-highlighter": "^15.5.2",
- "@types/semver": "^7.5.8",
- "@types/uglifyjs-webpack-plugin": "^1.1.2",
- "@types/underscore": "^1.11.15",
- "@types/webpack": "^5.28.5",
- "babel-loader": "^9.1.3",
- "buffer": "^6.0.3",
- "cache-loader": "^4.1.0",
- "commander": "^11.0.0",
- "css-loader": "^6.8.1",
- "css-minimizer-webpack-plugin": "^4.0.0",
- "dotenv": "^16.4.5",
- "file-loader": "^6.2.0",
- "filemanager-webpack-plugin": "^8.0.0",
- "fs-extra": "^11.2.0",
- "gh-pages": "^6.1.1",
- "html-webpack-plugin": "^5.6.0",
- "image-webpack-loader": "^8.1.0",
- "js-yaml-loader": "^1.2.2",
- "license-checker": "^25.0.1",
- "mini-css-extract-plugin": "^2.9.0",
- "postcss-loader": "^7.3.3",
- "raw-loader": "^4.0.2",
- "sass": "^1.49.8",
- "sass-loader": "^13.0.2",
- "style-loader": "^3.3.3",
- "terser-webpack-plugin": "^5.3.9",
- "thread-loader": "^4.0.2",
- "ts-loader": "^9.3.0",
- "ts-node": "^10.9.2",
- "tslib": "^2.4.0",
- "typescript": "^5.2.2",
- "url-loader": "^4.1.1",
- "vscode": "^1.1.37",
- "webpack": "^5.94.0",
- "webpack-cli": "^5.1.4",
- "webpack-dev-server": "^4.15.1",
- "yaml-loader": "^0.8.0"
- }
-}
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index de7bdb7f..00000000
--- a/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-rootProject.name = "MMRL"
-include ':app'
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 00000000..b13af60f
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,25 @@
+enableFeaturePreview("STABLE_CONFIGURATION_CACHE")
+enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
+
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ maven("https://jitpack.io")
+ }
+}
+
+pluginManagement {
+ includeBuild("build-logic")
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+rootProject.name = "MMRL"
+include(":app")
+include(":hidden-api")
+include(":compat")
\ No newline at end of file
diff --git a/src/activitys/AboutActivity.tsx b/src/activitys/AboutActivity.tsx
deleted file mode 100644
index 91dd441d..00000000
--- a/src/activitys/AboutActivity.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-import { Page } from "@Components/onsenui/Page";
-import { Toolbar } from "@Components/onsenui/Toolbar";
-import { useActivity } from "@Hooks/useActivity";
-import { useStrings } from "@Hooks/useStrings";
-import { useTheme } from "@Hooks/useTheme";
-
-import Badge from "@mui/material/Badge";
-import List from "@mui/material/List";
-import ListItem from "@mui/material/ListItem";
-import ListItemText from "@mui/material/ListItemText";
-import Avatar from "@mui/material/Avatar";
-import Typography from "@mui/material/Typography";
-import Stack from "@mui/material/Stack";
-import CodeRoundedIcon from "@mui/icons-material/CodeRounded";
-import { Shell } from "@Native/Shell";
-import { ListSubheader } from "@mui/material";
-import React from "react";
-import { BuildConfig } from "@Native/BuildConfig";
-import { useFormatDate } from "@Hooks/useFormatDate";
-
-const checkRoot = (): string | undefined => {
- if (Shell.isMagiskSU()) {
- return "assets/MagiskSULogo.png";
- } else if (Shell.isKernelSU()) {
- return "assets/KernelSULogo.png";
- } else if (Shell.isAPatchSU()) {
- return "assets/APatchSULogo.png";
- } else {
- return undefined;
- }
-};
-
-const AboutActivity = () => {
- const { strings } = useStrings();
- const { context } = useActivity();
-
- const renderToolbar = () => {
- return (
-
-
-
-
- {strings("about")}
-
- );
- };
-
- // false to ignore multiplying
- const date = useFormatDate(BuildConfig.BUILD_DATE, false);
-
- type ListRender = {
- title: string;
- content: Array<{ primary: string; secondary: string | number }>;
- };
-
- const list = React.useMemo(
- () => [
- {
- title: "App",
- content: [
- { primary: "Name", secondary: BuildConfig.APPLICATION_ID },
- { primary: "Version", secondary: `v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})` },
- { primary: "Build date", secondary: date },
- { primary: "Build type", secondary: BuildConfig.BUILD_TYPE },
- ],
- },
- {
- title: "Root",
- content: [
- { primary: "Root manager", secondary: Shell.getRootManager() },
- { primary: "Root version", secondary: `${Shell.VERSION_NAME().replace(/(.+):(.+)/gim, "$1")} (${Shell.VERSION_CODE()})` },
- ],
- },
- {
- title: "User",
- content: [
- { primary: "Name", secondary: Shell.pw_name() },
- { primary: "User ID", secondary: Shell.pw_uid() },
- { primary: "Group ID", secondary: Shell.pw_gid() },
- ],
- },
- ],
- []
- );
-
- return (
-
-
-
- ({
- width: 40,
- height: 40,
- borderRadius: "unset",
- bgcolor: "transparent",
- })}
- src={checkRoot()}
- >
- }
- >
-
-
-
-
-
-
- MMRL
-
-
- {list.map((l) => (
- {l.title} : undefined}
- >
- {l.content.map((c) => (
-
-
-
-
- {c.primary}
-
-
- {c.secondary}
-
-
- >
- }
- />
-
- ))}
-
- ))}
-
-
-
- );
-};
-
-export default AboutActivity;
diff --git a/src/activitys/CommentsActivity.tsx b/src/activitys/CommentsActivity.tsx
deleted file mode 100644
index ad9adf8f..00000000
--- a/src/activitys/CommentsActivity.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Page } from "@Components/onsenui/Page";
-import { Toolbar } from "@Components/onsenui/Toolbar";
-import { useActivity } from "@Hooks/useActivity";
-import { useStrings } from "@Hooks/useStrings";
-import ArrowBackIcon from "@mui/icons-material/ArrowBack";
-import { useSettings } from "@Hooks/useSettings";
-import Giscus from "@giscus/react";
-
-type Extra = {
- id: string;
-};
-
-const CommentsActivity = () => {
- const { strings } = useStrings();
- const [language] = useSettings("language");
- const { context, extra } = useActivity();
-
- const renderToolbar = () => {
- return (
-
-
-
-
- {strings("comments")}
-
- );
- };
-
- return (
-
-
-
-
-
- );
-};
-
-export { CommentsActivity };
diff --git a/src/activitys/DescriptonActivity.tsx b/src/activitys/DescriptonActivity.tsx
deleted file mode 100644
index 9f3ecf10..00000000
--- a/src/activitys/DescriptonActivity.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import { ProgressCircular } from "react-onsenui";
-import { Markup } from "@Components/Markdown";
-import { useActivity } from "@Hooks/useActivity";
-import { Toolbar } from "@Components/onsenui/Toolbar";
-import { Page } from "@Components/onsenui/Page";
-import { useStrings } from "@Hooks/useStrings";
-import { useTheme } from "@Hooks/useTheme";
-import CloseIcon from "@mui/icons-material/Close";
-import Box from "@mui/material/Box";
-import Avatar from "@mui/material/Avatar";
-import Typography from "@mui/material/Typography";
-
-type Extra = {
- desc?: string;
- name: string;
- logo?: string;
-};
-
-function DescriptonActivity() {
- const { context, extra } = useActivity();
- const { strings } = useStrings();
- const { theme } = useTheme();
- const { desc, name, logo } = extra;
-
- const renderToolbar = () => {
- return (
-
-
-
- ({
- bgcolor: theme.palette.primary.dark,
- width: 40,
- height: 40,
- boxShadow: "0 -1px 5px rgba(0,0,0,.09), 0 3px 5px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.3), 0 1px 3px rgba(0,0,0,.15)",
- borderRadius: "20%",
- mr: 1.5,
- fontSize: 14,
- })}
- src={logo}
- >
- {name.charAt(0).toUpperCase()}
-
-
-
-
- {name}
-
-
- {strings("about_this_module")}
-
-
-
-
-
-
-
-
- );
- };
-
- return (
-
-
- {!desc ? (
-
- ) : (
- <>
-
- >
- )}
-
-
- );
-}
-
-export default DescriptonActivity;
diff --git a/src/activitys/FetchTextActivity.tsx b/src/activitys/FetchTextActivity.tsx
deleted file mode 100644
index 069fbe4d..00000000
--- a/src/activitys/FetchTextActivity.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import { ProgressCircular } from "react-onsenui";
-import { Markup } from "@Components/Markdown";
-import { useActivity } from "@Hooks/useActivity";
-import React from "react";
-import { Toolbar } from "@Components/onsenui/Toolbar";
-import { Page } from "@Components/onsenui/Page";
-import { useNetwork } from "@Hooks/useNetwork";
-import { MissingInternet } from "@Components/MissingInternet";
-import { useFetch } from "@Hooks/useFetch";
-
-export type FetchTextActivityExtra = {
- rendering?: React.FunctionComponent | React.ComponentType;
- modulename?: string;
- title?: string;
- raw_data?: string;
- url?: string;
-};
-
-interface State {
- data?: T;
- error?: Error;
-}
-
-type Cache = { [url: string]: T };
-type Action = { type: "loading" } | { type: "fetched"; payload: T } | { type: "error"; payload: Error };
-
-function FetchTextActivity() {
- const { context, extra } = useActivity();
- const { isNetworkAvailable } = useNetwork();
- const { title, modulename, url } = extra;
-
- const [data] = useFetch(url, {
- type: "text",
- });
-
- const state = data || extra.raw_data;
-
- const renderToolbar = () => {
- return (
-
-
-
-
- {title || modulename}
-
- );
- };
-
- if (!isNetworkAvailable) {
- return (
-
-
-
- );
- }
-
- if (!state) {
- return (
-
-
-
- );
- }
-
- return (
-
- {!extra.rendering ? : }
-
- );
-}
-
-export default FetchTextActivity;
diff --git a/src/activitys/InstallTerminalV2Activity/hooks/useExploreInstall.tsx b/src/activitys/InstallTerminalV2Activity/hooks/useExploreInstall.tsx
deleted file mode 100644
index d293ffe8..00000000
--- a/src/activitys/InstallTerminalV2Activity/hooks/useExploreInstall.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-import { useActivity } from "@Hooks/useActivity";
-import { TerminalActivityExtra } from "..";
-import { v1 as uuidv1 } from "uuid";
-import { Download } from "@Native/Download";
-import React from "react";
-import { useLines } from "./useLines";
-import { useSettings } from "@Hooks/useSettings";
-import { Terminal } from "@Native/Terminal";
-import { BuildConfig } from "@Native/BuildConfig";
-import { Shell } from "@Native/Shell";
-import { RestartAlt } from "@mui/icons-material";
-import { SuFile } from "@Native/SuFile";
-
-type ExploreInstall = {
- url: string;
- printExit?: boolean;
-};
-
-const useExploreInstall = (): [(options: ExploreInstall) => Promise, number] => {
- const TMPDIR = "/data/local/tmp";
- const { extra } = useActivity();
- const [downloadProgress, setDownloadProgress] = React.useState(0);
- const [printTerminalError] = useSettings("print_terminal_error");
- const { addText, addButton, setLastLine, rebootDevice, getInstallCLI } = useLines();
- const { source, issues } = extra;
-
- const exploreInstaller = async (options: ExploreInstall): Promise => {
- return new Promise((resolve, reject) => {
- const url = options.url;
- const printExit = options.printExit ?? true;
- const modPath = `${TMPDIR}/${uuidv1()}.zip`;
-
- console.debug(modPath);
-
- const dl = new Download(url, modPath);
-
- // Handle download progress
- dl.onChange = (obj) => {
- switch (obj.type) {
- case "downloading":
- setDownloadProgress(obj.state);
- setLastLine(`- Downloading module progress: ${obj.state}%`);
- break;
- case "finished":
- setDownloadProgress(0);
-
- const explore_install = new Terminal({
- cwd: TMPDIR,
- printError: printTerminalError,
- });
-
- explore_install.env = {
- ASH_STANDALONE: "1",
- MMRL: "true",
- MMRL_INTR: "true",
- MMRL_VER: BuildConfig.VERSION_CODE.toString(),
- ROOTMANAGER: Shell.getRootManager(),
- };
-
- // Add terminal line output
- explore_install.onLine = (line) => {
- addText(line);
- };
-
- if (printTerminalError) {
- explore_install.onError = (err) => {
- addText(`\x1b[38;5;130mⓘ${err}`);
- };
- }
-
- explore_install.onExit = (code) => {
- SuFile.deleteRecursive(modPath);
-
- if (printExit) {
- switch (code) {
- case Shell.M_INS_SUCCESS:
- addText(" ");
- addText(
- "\x1b[93mYou can press the \x1b[33;4mbutton\x1b[93;0m\x1b[93m below to \x1b[33;4mreboot\x1b[93;0m\x1b[93m your device\x1b[0m"
- );
- addButton("Reboot", {
- startIcon: ,
- onClick: rebootDevice,
- });
- addText(
- "\x1b[2mModules that causes issues after installing belong not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to their support page\x1b[2m"
- );
- if (issues) {
- addText(`> \x1b[32mIssues: \x1b[33m${issues}\x1b[0m`);
- }
- if (source) {
- addText(`> \x1b[32mSource: \x1b[33m${source}\x1b[0m`);
- }
- break;
- case Shell.M_INS_FAILURE:
- addText(" ");
- addText(
- "\x1b[2mModules that cause issues after installing belong not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to their support page\x1b[2m"
- );
- if (issues) {
- addText(`> \x1b[32mIssues: \x1b[33m${issues}\x1b[0m`);
- }
- if (source) {
- addText(`> \x1b[32mSource: \x1b[33m${source}\x1b[0m`);
- }
- break;
- case Shell.TERM_INTR_ERR:
- addText("! \x1b[31mInternal error!\x1b[0m");
- break;
- default:
- addText(`? Unknown code returned (${code}})`);
- break;
- }
- }
-
- resolve(); // Resolve the promise once the terminal exits
- };
-
- try {
- // Execute the command but don't expect a return value
- explore_install.exec(getInstallCLI({ ZIPFILE: modPath }));
- } catch (err) {
- addText(`! \x1b[31mExecution error: ${err}\x1b[0m`);
- reject(err); // Reject the promise on execution error
- }
-
- break;
- }
- };
-
- // Handle download errors
- dl.onError = (err) => {
- setDownloadProgress(0);
- addText("! \x1b[31mUnable to download the module\x1b[0m");
- addText("! \x1b[31mERR: " + err + "\x1b[0m");
- reject(err); // Reject the promise on download error
- };
-
- // Start the download
- dl.start();
- });
- };
-
- return [exploreInstaller, downloadProgress];
-};
-
-export { useExploreInstall };
diff --git a/src/activitys/InstallTerminalV2Activity/hooks/useLines.tsx b/src/activitys/InstallTerminalV2Activity/hooks/useLines.tsx
deleted file mode 100644
index 29ff7c8e..00000000
--- a/src/activitys/InstallTerminalV2Activity/hooks/useLines.tsx
+++ /dev/null
@@ -1,259 +0,0 @@
-import { Ansi } from "@Components/Ansi";
-import { Image } from "@Components/dapi/Image";
-import { useModFS } from "@Hooks/useModFS";
-import { useStrings } from "@Hooks/useStrings";
-import Button from "@mui/material/Button";
-import { Shell } from "@Native/Shell";
-import { SuFile } from "@Native/SuFile";
-import { path } from "@Util/path";
-import { useConfirm } from "material-ui-confirm";
-import ModFS from "modfs";
-import React from "react";
-
-interface LinesContext {
- processCommand: (rawCommand: string) => string | undefined;
- lines: any[];
- setLines: React.Dispatch>;
- addButton: (text: string, props?: object) => void;
- addText: (text: string, props?: object) => void;
- setLastLine: (text: string, props?: object) => void;
- rebootDevice: (reason?: string) => void;
- getInstallCLI: (adds?: Record) => string;
- clearTerminal: () => void;
-}
-
-const LinesContext = React.createContext({
- processCommand(_rawCommand) {
- return "";
- },
- lines: [],
- setLines() {},
- addButton(_text, _props) {},
- addText(_text, _props) {},
- setLastLine(_text, _props) {},
- rebootDevice(reason) {},
- getInstallCLI(adds) {
- return "exit " + Shell.TERM_INTR_ERR;
- },
- clearTerminal() {},
-});
-
-const colors = {
- R: "\x1b[0m",
- BRIGHT: "\x1b[1m",
- DIM: "\x1b[2m",
- UNDERSCORE: "\x1b[4m",
- FG: {
- BLACK: "\x1b[30m",
- RED: "\x1b[31m",
- GREEN: "\x1b[32m",
- YELLOW: "\x1b[33m",
- BLUE: "\x1b[34m",
- MAGENTA: "\x1b[35m",
- CYAN: "\x1b[36m",
- WHITE: "\x1b[37m",
- GRAY: "\x1b[90m",
- },
- BG: {
- BLACK: "\x1b[40m",
- RED: "\x1b[41m",
- GREEN: "\x1b[42m",
- YELLOW: "\x1b[43m",
- BLUE: "\x1b[44m",
- MAGENTA: "\x1b[45m",
- CYAN: "\x1b[46m",
- WHITE: "\x1b[47m",
- GRAY: "\x1b[100m",
- },
-};
-
-interface LinesProviderProps extends React.PropsWithChildren {}
-
-const LinesProvider = (props: LinesProviderProps) => {
- const { strings } = useStrings();
- const { modFS, modFSParse } = useModFS();
- const [useInt, setUseInt] = React.useState(false);
- const [lines, setLines] = React.useState([]);
- const confirm = useConfirm();
- const { children } = props;
-
- const addText = (text: string, props?: object) => {
- const txt = processCommand(text);
-
- if (typeof txt === "string" && txt !== "undefined") {
- setLines((lines) => [
- ...lines,
- {
- component: Ansi,
- props: {
- children: txt,
- sx: {
- mr: 1,
- ml: 1,
- },
- linkify: true,
- ...props,
- },
- },
- ]);
- }
- };
-
- const setLastLine = (text: string, props?: object) => {
- setLines((p) => p.slice(0, -1));
- addText(text, props);
- };
-
- const addImage = (data: string, props?: object) => {
- if (typeof data === "string") {
- setLines((lines) => [
- ...lines,
- {
- component: Image,
- props: {
- src: data,
- noOpen: true,
- sx: {
- mr: 1,
- ml: 1,
- },
- ...props,
- },
- },
- ]);
- }
- };
-
- const addButton = (text: string, props?: object) => {
- setLines((lines) => [
- ...lines,
- {
- component: Button,
- props: {
- children: text,
- variant: "contained",
- sx: {
- width: "50vmin",
- mt: 1,
- mb: 1,
- },
- ...props,
- },
- },
- ]);
- };
-
- const format = React.useMemo(
- () => ({
- addImage(data: string) {
- addImage(data);
- return "undefined";
- },
- setLastLine(text: string) {
- if (typeof text === "undefined") return "undefined";
- setLastLine(text);
- return "undefined";
- },
- color: (text: string) => {
- if (typeof text === "undefined") return "undefined";
- return ModFS.format(text, colors);
- },
- clearTerminal: () => {
- setLines([]);
- return "undefined";
- },
- removeLastLine: () => {
- setLines((p) => p.slice(0, -1));
- return "undefined";
- },
- }),
- []
- );
-
- const processCommand = (rawCommand: string): string | "undefined" => {
- if (rawCommand.startsWith("#!mmrl:")) {
- rawCommand = rawCommand.substring(7);
- return ModFS.format(rawCommand, format) as string | "undefined";
- } else {
- const info = /^\-(\s+)?(.+)/gm;
- const warn = /^\?(\s+)?(.+)/gm;
- const erro = /^\!(\s+)?(.+)/gm;
-
- if (rawCommand.match(info)) {
- return rawCommand.replace(info, "$2");
- } else if (rawCommand.match(erro)) {
- return rawCommand.replace(erro, "\x1b[31m$2\x1b[0m");
- } else if (rawCommand.match(warn)) {
- return rawCommand.replace(warn, "\x1b[33m$2\x1b[0m");
- } else {
- return rawCommand;
- }
- }
- };
-
- const rebootDevice = React.useCallback((reason: string = "") => {
- confirm({
- title: strings("reboot_device"),
- description: strings("reboot_device_desc"),
- confirmationText: strings("yes"),
- cancellationText: strings("cancel"),
- }).then(() => {
- Shell.cmd(`/system/bin/svc power reboot ${reason} || /system/bin/reboot ${reason}`).exec();
- });
- }, []);
-
- const getInstallCLI = React.useCallback((adds?: Record) => {
- const __adds = {
- ...adds,
- findBinary(binaryNames: string, args: string) {
- const folders = ["/system/bin", "/ksu/bin", "/ap/bin", "/magisk"];
- const _binaryNames = binaryNames.split(",");
- for (const binaryName of _binaryNames) {
- for (const folder of folders) {
- const binaryPath = modFSParse(path.join(folder, binaryName));
- if (SuFile.exist(binaryPath)) {
- return `${binaryPath} ${args}`;
- }
- }
- }
- return null;
- },
- };
-
- switch (Shell.getRootManager()) {
- case "Magisk":
- return modFS("MSUINI", __adds);
- case "KernelSU":
- return modFS("KSUINI", __adds);
- case "APatchSU":
- return modFS("ASUINI", __adds);
- default:
- return `exit ${Shell.M_DWL_FAILURE}`;
- }
- }, []);
-
- const clearTerminal = () => {
- setLines([]);
- };
-
- const value = React.useMemo(
- () => ({
- processCommand: processCommand,
- lines: lines,
- setLines: setLines,
- addButton: addButton,
- addText: addText,
- setLastLine: setLastLine,
- rebootDevice,
- getInstallCLI,
- clearTerminal,
- }),
- [processCommand, lines, setLines, useInt, setUseInt, addButton, addText, setLastLine, rebootDevice, getInstallCLI, clearTerminal]
- );
-
- return ;
-};
-
-const useLines = () => React.useContext(LinesContext);
-
-export { useLines, LinesProvider };
diff --git a/src/activitys/InstallTerminalV2Activity/hooks/useLocalInstall.tsx b/src/activitys/InstallTerminalV2Activity/hooks/useLocalInstall.tsx
deleted file mode 100644
index aab7afe7..00000000
--- a/src/activitys/InstallTerminalV2Activity/hooks/useLocalInstall.tsx
+++ /dev/null
@@ -1,195 +0,0 @@
-import { useActivity } from "@Hooks/useActivity";
-import { TerminalActivityExtra } from "..";
-import { v1 as uuidv1 } from "uuid";
-import { Download } from "@Native/Download";
-import React from "react";
-import { useLines } from "./useLines";
-import { useSettings } from "@Hooks/useSettings";
-import { Terminal } from "@Native/Terminal";
-import { BuildConfig } from "@Native/BuildConfig";
-import { Shell } from "@Native/Shell";
-import { Add, Remove, CodeRounded, ArrowBackIosRounded, RestartAlt } from "@mui/icons-material";
-
-// const useLocalInstall = (): [() => void] => {
-// const TMPDIR = "/data/local/tmp";
-
-// const { extra } = useActivity();
-// const [printTerminalError] = useSettings("print_terminal_error");
-
-// const { addText, addButton, rebootDevice, getInstallCLI } = useLines();
-
-// const { modSource } = extra;
-
-// return [
-// () => {
-// const zipfile = modSource[0];
-// // const zipfiles = modSource;
-
-// const local_install = new Terminal({
-// cwd: TMPDIR,
-// printError: printTerminalError,
-// });
-
-// local_install.env = {
-// ASH_STANDALONE: "1",
-// MMRL: "true",
-// MMRL_INTR: "true",
-// MMRL_VER: BuildConfig.VERSION_CODE.toString(),
-// ROOTMANAGER: Shell.getRootManager(),
-// };
-
-// local_install.onLine = (line) => {
-// addText(line);
-// };
-
-// if (printTerminalError) {
-// local_install.onError = (err) => {
-// addText(`\x1b[38;5;130mⓘ${err}`);
-// };
-// }
-
-// local_install.onExit = (code) => {
-// switch (code) {
-// case Shell.M_INS_SUCCESS:
-// addText(" ");
-
-// addText(
-// "\x1b[93mYou can press the \x1b[33;4mbutton\x1b[93;0m\x1b[93m below to \x1b[33;4mreboot\x1b[93;0m\x1b[93m your device\x1b[0m"
-// );
-
-// addButton("Reboot", {
-// startIcon: ,
-// onClick: rebootDevice,
-// });
-
-// addText(
-// "\x1b[2mModules that causes issues after installing belog not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to thier support page\x1b[2m"
-// );
-// break;
-
-// case Shell.M_INS_FAILURE:
-// addText(" ");
-
-// addText(
-// "\x1b[2mModules that causes issues after installing belog not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to thier support page\x1b[2m"
-// );
-// break;
-
-// case Shell.TERM_INTR_ERR:
-// addText("! \x1b[31mInternal error!\x1b[0m");
-// break;
-
-// default:
-// addText("- Unknown code returned");
-// break;
-// }
-// };
-
-// local_install.exec(
-// getInstallCLI({
-// ZIPFILE: zipfile,
-// })
-// );
-// },
-// ];
-// };
-
-// export { useLocalInstall };
-
-type LocalInstall = {
- file: string;
- printExit?: boolean;
-};
-
-const useLocalInstall = (): [(options: LocalInstall) => Promise] => {
- const TMPDIR = "/data/local/tmp";
- const { extra } = useActivity();
- const [printTerminalError] = useSettings("print_terminal_error");
- const { addText, addButton, rebootDevice, getInstallCLI } = useLines();
- const { modSource } = extra;
-
- const localInstaller = async (options: LocalInstall): Promise => {
- return new Promise((resolve, reject) => {
- const zipfile = options.file;
- const printExit = options.printExit ?? true;
-
- const local_install = new Terminal({
- cwd: TMPDIR,
- printError: printTerminalError,
- });
-
- local_install.env = {
- ASH_STANDALONE: "1",
- MMRL: "true",
- MMRL_INTR: "true",
- MMRL_VER: BuildConfig.VERSION_CODE.toString(),
- ROOTMANAGER: Shell.getRootManager(),
- };
-
- // Handle terminal output lines
- local_install.onLine = (line) => {
- addText(line);
- };
-
- // Handle terminal errors
- if (printTerminalError) {
- local_install.onError = (err) => {
- addText(`\x1b[38;5;130mⓘ${err}`);
- };
- }
-
- // Handle terminal exit with a promise resolution
- local_install.onExit = (code) => {
- if (printExit) {
- switch (code) {
- case Shell.M_INS_SUCCESS:
- addText(" ");
- addText(
- "\x1b[93mYou can press the \x1b[33;4mbutton\x1b[93;0m\x1b[93m below to \x1b[33;4mreboot\x1b[93;0m\x1b[93m your device\x1b[0m"
- );
- addButton("Reboot", {
- startIcon: ,
- onClick: rebootDevice,
- });
- addText(
- "\x1b[2mModules that causes issues after installing belong not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to their support page\x1b[2m"
- );
- break;
-
- case Shell.M_INS_FAILURE:
- addText(" ");
- addText(
- "\x1b[2mModules that causes issues after installing belong not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to their support page\x1b[2m"
- );
- break;
-
- case Shell.TERM_INTR_ERR:
- addText("! \x1b[31mInternal error!\x1b[0m");
- break;
-
- default:
- addText(`? Unknown code returned (${code}})`);
- break;
- }
- }
- resolve(); // Resolve on unknown code, as execution has finished
- };
-
- // Execute the installation command
- try {
- local_install.exec(
- getInstallCLI({
- ZIPFILE: zipfile,
- })
- );
- } catch (err) {
- addText(`! \x1b[31mExecution error: ${err}\x1b[0m`);
- reject(err); // Reject on execution error
- }
- });
- };
-
- return [localInstaller];
-};
-
-export { useLocalInstall };
diff --git a/src/activitys/InstallTerminalV2Activity/index.tsx b/src/activitys/InstallTerminalV2Activity/index.tsx
deleted file mode 100644
index dc53c978..00000000
--- a/src/activitys/InstallTerminalV2Activity/index.tsx
+++ /dev/null
@@ -1,202 +0,0 @@
-import { Page } from "@Components/onsenui/Page";
-import { Toolbar } from "@Components/onsenui/Toolbar";
-import { useActivity } from "@Hooks/useActivity";
-import { useSettings } from "@Hooks/useSettings";
-import { alpha, Box, LinearProgress, Stack, Typography } from "@mui/material";
-import { view, WindowManager } from "@Native/View";
-import FlatList from "flatlist-react";
-import React from "react";
-import { useExploreInstall } from "./hooks/useExploreInstall";
-import { LinesProvider, useLines } from "./hooks/useLines";
-import { useLocalInstall } from "./hooks/useLocalInstall";
-
-export interface TerminalActivityExtra {
- exploreInstall: boolean;
- modSource: string[];
- source?: string;
- issues?: string;
-}
-
-const InstallerComponent = () => {
- const { context, extra } = useActivity();
-
- const termEndRef = React.useRef(null);
-
- const { clearTerminal, lines } = useLines();
-
- const [termScrollBottom] = useSettings("term_scroll_bottom");
- const [termScrollBehavior] = useSettings("term_scroll_behavior");
-
- const [terminalWordWrap] = useSettings("terminal_word_wrap");
- const [terminalNumbericLines] = useSettings("terminal_numberic_lines");
-
- if (termScrollBottom) {
- const termBehavior = React.useMemo(() => termScrollBehavior, [termScrollBehavior]);
-
- React.useEffect(() => {
- termEndRef.current?.scrollIntoView({ behavior: termBehavior.value, block: "end", inline: "nearest" });
- }, [lines]);
- }
-
- const [exploreInstaller, downloadProgress] = useExploreInstall();
- const [localInstaller] = useLocalInstall();
-
- const handleExploreInstall = React.useCallback(async () => {
- const { modSource } = extra;
-
- for (let idx = 0; idx < modSource.length; idx++) {
- const mod = modSource[idx];
- const isLastItem = idx === modSource.length - 1;
- const shouldPrintExit = modSource.length === 1 || isLastItem;
-
- try {
- await exploreInstaller({ url: mod, printExit: shouldPrintExit });
- } catch (error) {
- console.error("An error occurred during installation:", error);
- }
- }
- }, []);
-
- const handleLocalInstall = React.useCallback(async () => {
- const { modSource } = extra;
-
- for (let idx = 0; idx < modSource.length; idx++) {
- const mod = modSource[idx];
- const isLastItem = idx === modSource.length - 1;
- const shouldPrintExit = modSource.length === 1 || isLastItem;
-
- try {
- await localInstaller({ file: mod, printExit: shouldPrintExit });
- } catch (error) {
- console.error("An error occurred during installation:", error);
- }
- }
- }, []);
-
- // ensure that it is always the same function
- const nativeVolumeEventPrevent = React.useCallback((e: Event) => {
- e.preventDefault();
- }, []);
- React.useEffect(() => {
- const { exploreInstall } = extra;
- if (exploreInstall) {
- handleExploreInstall();
- } else {
- handleLocalInstall();
- }
-
- document.addEventListener("volumeupbutton", nativeVolumeEventPrevent, false);
- document.addEventListener("volumedownbutton", nativeVolumeEventPrevent, false);
- view.addFlags([WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON]);
- return () => {
- document.removeEventListener("volumeupbutton", nativeVolumeEventPrevent, false);
- document.removeEventListener("volumedownbutton", nativeVolumeEventPrevent, false);
- view.clearFlags([WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON]);
- };
- }, []);
-
- const renderToolbar = () => {
- return (
-
-
-
-
- Install
- {downloadProgress !== 0 && (
-
- )}
-
- );
- };
-
- return (
-
-
-
- (
-
- {terminalNumbericLines && (
- ({
- minWidth: "40px",
- paddingRight: "1em",
- fontSize: "unset",
- marginLeft: "calc(18px - 1em)",
- color: theme.palette.text.secondary,
- textAlign: "right",
- textDecoration: "none",
- })}
- >
- {Number(key) + 1}
-
- )}
-
-
-
- )}
- renderOnScroll
- renderWhenEmpty={() => <>>}
- />
-
-
-
-
- );
-};
-
-export const InstallTerminalV2Activity = () => {
- return (
-
-
-
- );
-};
-
-export default InstallTerminalV2Activity;
diff --git a/src/activitys/LandingActivity/components/FAQ.tsx b/src/activitys/LandingActivity/components/FAQ.tsx
deleted file mode 100644
index 16e7cda8..00000000
--- a/src/activitys/LandingActivity/components/FAQ.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import * as React from "react";
-import { alpha, styled, SxProps } from "@mui/material/styles";
-import ArrowForwardIosSharpIcon from "@mui/icons-material/ArrowForwardIosSharp";
-import MuiAccordion, { AccordionProps } from "@mui/material/Accordion";
-import MuiAccordionSummary, { AccordionSummaryProps } from "@mui/material/AccordionSummary";
-import MuiAccordionDetails from "@mui/material/AccordionDetails";
-import Typography from "@mui/material/Typography";
-import { Markup } from "@Components/Markdown";
-import { Box } from "@mui/material";
-
-const Accordion = styled((props: AccordionProps) => )(({ theme }) => ({
- border: `1px solid ${theme.palette.divider}`,
-
- backgroundColor: alpha(theme.palette.background.paper, 0.6),
- backdropFilter: "saturate(180%) blur(20px)",
- "&:not(:last-child)": {
- borderBottom: 0,
- },
- "&:last-child": {
- borderRadius: "0px 0px 8px 8px",
- },
- "&:first-child": {
- borderRadius: "8px 8px 0px 0px",
- },
- "&::before": {
- display: "none",
- },
-}));
-
-const AccordionSummary = styled((props: AccordionSummaryProps) => (
- } {...props} />
-))(({ theme }) => ({
- backgroundColor: "none",
-
- flexDirection: "row-reverse",
- "& .MuiAccordionSummary-expandIconWrapper.Mui-expanded": {
- transform: "rotate(90deg)",
- },
- "& .MuiAccordionSummary-content": {
- marginLeft: theme.spacing(1),
- },
-}));
-
-const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
- padding: theme.spacing(2),
- backgroundColor: "none",
-}));
-
-interface FAQItem {
- q: string;
- a: string;
-}
-interface FAQProps {
- items: FAQItem[];
- sx?: SxProps;
-}
-
-const FAQ = (props: FAQProps) => {
- const [expanded, setExpanded] = React.useState(false);
-
- const handleChange = (panel: number) => (_: React.SyntheticEvent, newExpanded: boolean) => {
- setExpanded(newExpanded ? panel : false);
- };
-
- return (
-
- {props.items.map((item, index) => (
-
-
-
- {item.a}
-
-
- ))}
-
- );
-};
-
-export { FAQ };
diff --git a/src/activitys/LandingActivity/components/GridCard.tsx b/src/activitys/LandingActivity/components/GridCard.tsx
deleted file mode 100644
index ca97b6da..00000000
--- a/src/activitys/LandingActivity/components/GridCard.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { useTheme } from "@Hooks/useTheme";
-import { alpha } from "@mui/material";
-import Card from "@mui/material/Card";
-import Typography from "@mui/material/Typography";
-import Grid from "@mui/material/Grid";
-
-interface GridCardProps {
- title?: string;
- description?: string;
-}
-
-const GridCard = (props: GridCardProps) => {
- const { theme } = useTheme();
-
- return (
-
-
- {props.title}
- {props.description}
-
-
- );
-};
-
-export { GridCard, GridCardProps };
diff --git a/src/activitys/LandingActivity/components/GridImage.tsx b/src/activitys/LandingActivity/components/GridImage.tsx
deleted file mode 100644
index 0a83f57f..00000000
--- a/src/activitys/LandingActivity/components/GridImage.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Image } from "@Components/dapi/Image";
-import { useTheme } from "@Hooks/useTheme";
-import { alpha } from "@mui/material";
-import Card from "@mui/material/Card";
-import Grid from "@mui/material/Grid";
-
-interface GridImageProps {
- src?: string;
- alt?: string;
-}
-
-const GridImage = (props: GridImageProps) => {
- const { theme } = useTheme();
-
- return (
-
-
-
-
-
- );
-};
-
-export { GridImage, GridImageProps };
diff --git a/src/activitys/LandingActivity/components/LandingToolbar.tsx b/src/activitys/LandingActivity/components/LandingToolbar.tsx
deleted file mode 100644
index 2a1b7fca..00000000
--- a/src/activitys/LandingActivity/components/LandingToolbar.tsx
+++ /dev/null
@@ -1,165 +0,0 @@
-import * as React from "react";
-import AppBar from "@mui/material/AppBar";
-import Box from "@mui/material/Box";
-import Toolbar from "@mui/material/Toolbar";
-import IconButton from "@mui/material/IconButton";
-import Typography from "@mui/material/Typography";
-import Menu from "@mui/material/Menu";
-import MenuIcon from "@mui/icons-material/Menu";
-import Container from "@mui/material/Container";
-import Avatar from "@mui/material/Avatar";
-import Button from "@mui/material/Button";
-import Tooltip from "@mui/material/Tooltip";
-import MenuItem from "@mui/material/MenuItem";
-import CodeRoundedIcon from "@mui/icons-material/CodeRounded";
-import GitHubIcon from "@mui/icons-material/GitHub";
-import { os } from "@Native/Os";
-import { StyledMenu } from "@Components/DropdownButton";
-import { useTheme } from "@Hooks/useTheme";
-import { alpha } from "@mui/material";
-import { view } from "@Native/View";
-
-interface LandingPageMenuItem {
- title: string;
- onClick?: React.MouseEventHandler;
-}
-
-interface LandigToolbarProps {
- menuItems: LandingPageMenuItem[];
-}
-
-function LandingToolbar(props: LandigToolbarProps) {
- const { theme } = useTheme();
- const [anchorElNav, setAnchorElNav] = React.useState(null);
-
- const handleOpenNavMenu = (event: React.MouseEvent) => {
- setAnchorElNav(event.currentTarget);
- };
-
- const handleCloseNavMenu = () => {
- setAnchorElNav(null);
- };
-
- return (
-
-
-
-
-
- MMRL
-
-
-
-
-