diff --git a/module/webui/index.html b/module/webui/index.html
index 4379259..444508e 100644
--- a/module/webui/index.html
+++ b/module/webui/index.html
@@ -76,7 +76,8 @@
-
+
+
@@ -185,6 +186,13 @@
+
+
+
+
+
diff --git a/module/webui/locales/A-template.json b/module/webui/locales/A-template.json
index 2a173fb..601813d 100644
--- a/module/webui/locales/A-template.json
+++ b/module/webui/locales/A-template.json
@@ -17,6 +17,8 @@
"deselect_unnecessary_description": "Unnecessary category: Xposed module, root manager, root-related apps, and general apps that never check bootloader status. This option requires Internet connection.",
"set_keybox": "Set AOSP & Valid Keybox",
"set_keybox_description": "Replace tricky store keybox.xml. AOSP keybox will be replaced if there's no more valid keybox. Valid keybox option requires Internet connection.",
+ "set_custom_keybox": "Set Custom Keybox",
+ "set_custom_keybox_description": "Import keybox from your device storage. Only support xml file.",
"set_verified_boot_hash": "Set Verified Boot Hash",
"set_verified_boot_hash_description": "Get verifiedBootHash value from Key Attestation Demo. Fix abnormal boot state by resetting ro.boot.vbmeta.digest."
},
@@ -45,6 +47,7 @@
"deselect_unnecessary": "Deselect Unnecessary",
"set_aosp_keybox": "Set AOSP Keybox",
"set_valid_keybox": "Set Valid Keybox",
+ "set_custom_keybox": "Set Custom Keybox",
"set_verified_boot_hash": "Set Verified Boot Hash",
"about": "About"
},
@@ -81,6 +84,9 @@
"installed": "Installed successfully, reboot now.",
"install_fail": "Fail to install, please update manual",
"rebooting": "Rebooting...",
- "reboot_fail": "Fail to reboot, please reboot manually"
+ "reboot_fail": "Fail to reboot, please reboot manually",
+ "custom_key_set": "Custom keybox set successfully",
+ "custom_key_set_error": "Failed to set custom keybox",
+ "no_file_selected": "No file selected"
}
}
\ No newline at end of file
diff --git a/module/webui/locales/en-US.json b/module/webui/locales/en-US.json
index 2a173fb..601813d 100644
--- a/module/webui/locales/en-US.json
+++ b/module/webui/locales/en-US.json
@@ -17,6 +17,8 @@
"deselect_unnecessary_description": "Unnecessary category: Xposed module, root manager, root-related apps, and general apps that never check bootloader status. This option requires Internet connection.",
"set_keybox": "Set AOSP & Valid Keybox",
"set_keybox_description": "Replace tricky store keybox.xml. AOSP keybox will be replaced if there's no more valid keybox. Valid keybox option requires Internet connection.",
+ "set_custom_keybox": "Set Custom Keybox",
+ "set_custom_keybox_description": "Import keybox from your device storage. Only support xml file.",
"set_verified_boot_hash": "Set Verified Boot Hash",
"set_verified_boot_hash_description": "Get verifiedBootHash value from Key Attestation Demo. Fix abnormal boot state by resetting ro.boot.vbmeta.digest."
},
@@ -45,6 +47,7 @@
"deselect_unnecessary": "Deselect Unnecessary",
"set_aosp_keybox": "Set AOSP Keybox",
"set_valid_keybox": "Set Valid Keybox",
+ "set_custom_keybox": "Set Custom Keybox",
"set_verified_boot_hash": "Set Verified Boot Hash",
"about": "About"
},
@@ -81,6 +84,9 @@
"installed": "Installed successfully, reboot now.",
"install_fail": "Fail to install, please update manual",
"rebooting": "Rebooting...",
- "reboot_fail": "Fail to reboot, please reboot manually"
+ "reboot_fail": "Fail to reboot, please reboot manually",
+ "custom_key_set": "Custom keybox set successfully",
+ "custom_key_set_error": "Failed to set custom keybox",
+ "no_file_selected": "No file selected"
}
}
\ No newline at end of file
diff --git a/module/webui/locales/es-ES.json b/module/webui/locales/es-ES.json
index c5f4446..021bb45 100644
--- a/module/webui/locales/es-ES.json
+++ b/module/webui/locales/es-ES.json
@@ -17,6 +17,8 @@
"deselect_unnecessary_description": "Categorías innecesarias: módulos Xposed, gestores de root, aplicaciones relacionadas con root y aplicaciones generales que nunca verifican el estado del bootloader. Esta opción requiere conexión a Internet.",
"set_keybox": "Configurar AOSP y Keybox Válido",
"set_keybox_description": "Reemplazar el archivo keybox.xml de Tricky Store. El AOSP Keybox será reemplazado si no hay un keybox válido. Esta opción requiere conexión a Internet.",
+ "set_custom_keybox": "Establecer Keybox Personalizado",
+ "set_custom_keybox_description": "Importar keybox desde el almacenamiento de tu dispositivo. Solo soporta archivos xml.",
"set_verified_boot_hash": "Configurar Boot Hash Verificado",
"set_verified_boot_hash_description": "Obtén el valor de verifiedBootHash del Key Attestation Demo. Corrige un estado de arranque anormal reiniciando ro.boot.vbmeta.digest."
},
@@ -45,6 +47,7 @@
"deselect_unnecessary": "Deseleccionar innecesarios",
"set_aosp_keybox": "Configurar AOSP Keybox",
"set_valid_keybox": "Configurar Keybox Válido",
+ "set_custom_keybox": "Establecer Keybox Personalizado",
"set_verified_boot_hash": "Configurar Boot Hash Verificado",
"about": "Acerca de"
},
@@ -81,6 +84,9 @@
"installed": "Instalado con éxito, reinicia ahora.",
"install_fail": "Error al instalar, actualiza manualmente",
"rebooting": "Reiniciando...",
- "reboot_fail": "Error al reiniciar, reinicia manualmente"
+ "reboot_fail": "Error al reiniciar, reinicia manualmente",
+ "custom_key_set": "Keybox personalizado establecido con éxito",
+ "custom_key_set_error": "Error al establecer el keybox personalizado",
+ "no_file_selected": "Ningún archivo seleccionado"
}
}
\ No newline at end of file
diff --git a/module/webui/locales/ja-JP.json b/module/webui/locales/ja-JP.json
index 71244eb..0e52b07 100644
--- a/module/webui/locales/ja-JP.json
+++ b/module/webui/locales/ja-JP.json
@@ -17,6 +17,8 @@
"deselect_unnecessary_description": "不要なカテゴリー: Xposed モジュール、root マネージャー、root 関連アプリ、Bootloader の状態を確認しない一般的なアプリです。このオプションはインターネット接続が必要です。",
"set_keybox": "AOSP と 有効な Keybox",
"set_keybox_description": "Tricky Store の keybox.xml を置き換えます。有効な Keybox がなくなった場合は、AOSP Keybox に置き換えられます。インターネット接続が必要です。",
+ "set_custom_keybox": "カスタム Keybox を設定",
+ "set_custom_keybox_description": "デバイスのストレージからカスタム Keybox ファイルをインポートします。xml ファイルのみ対応。",
"set_verified_boot_hash": "確認付きブートハッシュを設定",
"set_verified_boot_hash_description": "Key Attestation Demo から確認付きブートハッシュの値を取得します。ro.boot.vbmeta.digest をリセットして異常なブート状態を修正します。"
},
@@ -45,6 +47,7 @@
"deselect_unnecessary": "不要な選択を解除",
"set_aosp_keybox": "AOSP Keybox を設定",
"set_valid_keybox": "有効な Keybox を設定",
+ "set_custom_keybox": "カスタム Keybox を設定",
"set_verified_boot_hash": "確認付きブートハッシュを設定",
"about": "このアドオンについて"
},
@@ -81,6 +84,9 @@
"installed": "正常にインストールされました。再起動してください。",
"install_fail": "インストールに失敗しました。手動で更新してください。",
"rebooting": "再起動中...",
- "reboot_fail": "再起動に失敗しました。手動で再起動してください。"
+ "reboot_fail": "再起動に失敗しました。手動で再起動してください。",
+ "custom_key_set": "カスタム Keybox の設定に成功しました",
+ "custom_key_set_error": "カスタム Keybox の設定に失敗しました",
+ "no_file_selected": "ファイルが選択されていません"
}
-}
+}
\ No newline at end of file
diff --git a/module/webui/locales/ru-RU.json b/module/webui/locales/ru-RU.json
index 522c77c..3874729 100644
--- a/module/webui/locales/ru-RU.json
+++ b/module/webui/locales/ru-RU.json
@@ -17,6 +17,8 @@
"deselect_unnecessary_description": "Ненужные категории: модули Xposed, менеджеры root, приложения, связанные с root, и общие приложения, которые никогда не проверяют статус загрузчика. Этот параметр требует подключения к интернету.",
"set_keybox": "Установить AOSP и действующий Keybox",
"set_keybox_description": "Замените tricky store keybox.xml. AOSP keybox будет заменен, если не будет найден действующий keybox. Опция с действующим keybox требует подключения к интернету.",
+ "set_custom_keybox": "Установить пользовательский Keybox",
+ "set_custom_keybox_description": "Импортируйте файл keybox из вашего устройства в хранилище. Поддерживаются только xml файлы.",
"set_verified_boot_hash": "Установить Verified Boot Hash",
"set_verified_boot_hash_description": "Получите значение verifiedBootHash из Key Attestation Demo. Исправьте аномальное состояние загрузки, сбросив ro.boot.vbmeta.digest."
},
@@ -45,6 +47,7 @@
"deselect_unnecessary": "Отменить выбор ненужных",
"set_aosp_keybox": "Установить AOSP Keybox",
"set_valid_keybox": "Установить действующий Keybox",
+ "set_custom_keybox": "Установить пользовательский Keybox",
"set_verified_boot_hash": "Установить Verified Boot Hash",
"about": "О программе"
},
@@ -81,6 +84,9 @@
"installed": "Успешно установлено, перезагрузите устройство.",
"install_fail": "Не удалось установить, обновите вручную",
"rebooting": "Перезагрузка...",
- "reboot_fail": "Не удалось перезагрузить, перезагрузите вручную"
+ "reboot_fail": "Не удалось перезагрузить, перезагрузите вручную",
+ "custom_key_set": "Пользовательский keybox успешно установлен",
+ "custom_key_set_error": "Не удалось установить пользовательский keybox",
+ "no_file_selected": "Файл не выбран"
}
}
\ No newline at end of file
diff --git a/module/webui/locales/tl-PH.json b/module/webui/locales/tl-PH.json
index e5421ed..5619e35 100644
--- a/module/webui/locales/tl-PH.json
+++ b/module/webui/locales/tl-PH.json
@@ -16,7 +16,9 @@
"deselect_unnecessary": "Huwag Pumili ng Hindi Kinakailangan",
"deselect_unnecessary_description": "Hindi kinakailangang kategorya: Xposed module, root manager, root-related apps, at mga karaniwang apps na hindi kailanman nire-refresh ang bootloader status. Nangangailangan ng koneksyon sa internet.",
"set_keybox": "I-set ang AOSP at Valid Keybox",
- "set_keybox_description": "Palitan ang tricky store keybox.xml. Palitan ang AOSP keybox kung walang valid keybox. Nangangailangan ng koneksyon sa internet ang valid keybox option.",
+ "set_keybox_description": "Palitan ang tricky store keybox. Palitan ang AOSP keybox kung walang valid keybox. Nangangailangan ng koneksyon sa internet ang valid keybox option.",
+ "set_custom_keybox": "I-set ang Custom Keybox",
+ "set_custom_keybox_description": "Mag-import ng custom keybox mula sa iyong device storage. Sumusuporta lamang ng xml file.",
"set_verified_boot_hash": "I-set ang Verified Boot Hash",
"set_verified_boot_hash_description": "Kunin ang verifiedBootHash mula sa Key Attestation Demo. Ayusin ang abnormal na boot state sa pamamagitan ng pag-reset ng ro.boot.vbmeta.digest."
},
@@ -45,6 +47,7 @@
"deselect_unnecessary": "Huwag Pumili ng Hindi Kinakailangan",
"set_aosp_keybox": "I-set ang AOSP Keybox",
"set_valid_keybox": "I-set ang Valid Keybox",
+ "set_custom_keybox": "I-set ang Custom Keybox",
"set_verified_boot_hash": "I-set ang Verified Boot Hash",
"about": "Tungkol"
},
@@ -81,6 +84,9 @@
"installed": "Matagumpay na na-install, mag-reboot na ngayon.",
"install_fail": "Nabigo ang pag-install, pakisubukang mag-update nang manu-mano",
"rebooting": "Nag-re-reboot...",
- "reboot_fail": "Nabigo ang pag-reboot, pakisubukang mag-reboot nang manu-mano"
+ "reboot_fail": "Nabigo ang pag-reboot, pakisubukang mag-reboot nang manu-mano",
+ "custom_key_set": "Matagumpay na na-set ang Custom Keybox",
+ "custom_key_set_error": "Nabigong i-set ang Custom Keybox",
+ "no_file_selected": "Walang napiling file"
}
}
\ No newline at end of file
diff --git a/module/webui/locales/tr-TR.json b/module/webui/locales/tr-TR.json
index a8d3f84..37255e9 100644
--- a/module/webui/locales/tr-TR.json
+++ b/module/webui/locales/tr-TR.json
@@ -12,13 +12,15 @@
"select_deselect": "Tümünü Seç & Seçimi Kaldır",
"select_description": "Mevcut arayüzdeki tüm uygulamaları seç veya seçimini kaldır.",
"select_denylist": "Reddetme Listesinden Seç",
- "select_denylist_description": "Yalnızca Magisk’te mevcut, Reddetme Listesindeki uygulamaları seç. Tavsiye edilir.",
+ "select_denylist_description": "Yalnızca Magisk'te mevcut, Reddetme Listesindeki uygulamaları seç. Tavsiye edilir.",
"deselect_unnecessary": "Gereksizleri Seçme",
"deselect_unnecessary_description": "Gereksiz kategori: Xposed modülü, root yöneticisi, root ile ilgili uygulamalar ve asla bootloader durumunu kontrol etmeyen genel uygulamalar. Bu seçenek internet bağlantısı gerektirir.",
"set_keybox": "AOSP & Geçerli Keybox Ayarla",
"set_keybox_description": "Tricky Store'daki keybox.xml dosyasını değiştirir. Eğer geçerli bir keybox yoksa AOSP keybox ile değiştirilecektir. Geçerli keybox seçeneği internet bağlantısı gerektirir.",
+ "set_custom_keybox": "Özel Keybox Ayarla",
+ "set_custom_keybox_description": "Cihaz depolamasından bir keybox dosyasını içe aktarın. Sadece xml dosyaları desteklenir.",
"set_verified_boot_hash": "Doğrulanmış Boot Hash Ayarla",
- "set_verified_boot_hash_description": "Key Attestation Demo’dan verifiedBootHash değerini alın. Abnormal boot durumunu ro.boot.vbmeta.digest’i sıfırlayarak düzeltin."
+ "set_verified_boot_hash_description": "Key Attestation Demo'dan verifiedBootHash değerini alın. Abnormal boot durumunu ro.boot.vbmeta.digest'i sıfırlayarak düzeltin."
},
"update": {
"update_available": "Yeni bir sürüm hazır",
@@ -32,7 +34,7 @@
},
"functional_button": {
"save_and_update_button": "Kaydet",
- "uninstall_webui": "WebUI’ı Kaldır"
+ "uninstall_webui": "WebUI'ı Kaldır"
},
"loading": {
"loading": "Yükleniyor..."
@@ -45,11 +47,12 @@
"deselect_unnecessary": "Gereksizleri Seçme",
"set_aosp_keybox": "AOSP Keybox Ayarla",
"set_valid_keybox": "Geçerli Keybox Ayarla",
+ "set_custom_keybox": "Özel Keybox Ayarla",
"set_verified_boot_hash": "Doğrulanmış Boot Hash Ayarla",
"about": "Hakkında"
},
"reset_vbmeta": {
- "boot_hash_input_placeholder": "Doğrulanmış Boot Hash’inizi buraya yapıştırın",
+ "boot_hash_input_placeholder": "Doğrulanmış Boot Hash'inizi buraya yapıştırın",
"boot_hash_save_button": "Kaydet"
},
"about": {
@@ -58,7 +61,7 @@
"by": "tarafından",
"telegram_channel": "Telegram Kanalı",
"github": "GitHub",
- "disclaimer": "Bu modül, Tricky Store modülünün bir parçası değildir. Herhangi bir sorun yaşarsanız, lütfen bunu Tricky Store’a rapor etmeyin.",
+ "disclaimer": "Bu modül, Tricky Store modülünün bir parçası değildir. Herhangi bir sorun yaşarsanız, lütfen bunu Tricky Store'a rapor etmeyin.",
"acknowledgment": "Teşekkür"
},
"prompt": {
@@ -81,6 +84,9 @@
"installed": "Başarıyla yüklendi, şimdi yeniden başlatın.",
"install_fail": "Yükleme başarısız oldu, lütfen manuel olarak güncelleyin",
"rebooting": "Yeniden başlatılıyor...",
- "reboot_fail": "Yeniden başlatma başarısız, lütfen manuel olarak yeniden başlatın"
+ "reboot_fail": "Yeniden başlatma başarısız, lütfen manuel olarak yeniden başlatın",
+ "custom_key_set": "Özel keybox başarıyla ayarlandı",
+ "custom_key_set_error": "Özel keybox ayarlanamadı",
+ "no_file_selected": "Dosya seçilmedi"
}
}
\ No newline at end of file
diff --git a/module/webui/locales/zh-CN.json b/module/webui/locales/zh-CN.json
index 3240d71..1e4857b 100644
--- a/module/webui/locales/zh-CN.json
+++ b/module/webui/locales/zh-CN.json
@@ -17,6 +17,8 @@
"deselect_unnecessary_description": "非必要分类:Xposed 模块、root 管理器、与 root 相关的应用,以及从不检查 bootloader 状态的通用应用。此功能需连网使用。",
"set_keybox": "设置 AOSP & 有效密钥",
"set_keybox_description": "替换 Tricky Store 的密钥(keybox.xml)。如果没有有效密钥,将替换为 AOSP 密钥。有效密钥选项需连网使用。",
+ "set_custom_keybox": "设置自定义密钥",
+ "set_custom_keybox_description": "从设备存储导入自定义密钥。仅支持 xml 文件。",
"set_verified_boot_hash": "设置哈希值",
"set_verified_boot_hash_description": "从 Key Attestation Demo 获取 verifiedBootHash(哈希值)。通过重置 ro.boot.vbmeta.digest 修复异常 boot 状态。"
},
@@ -45,6 +47,7 @@
"deselect_unnecessary": "取消选择非必应用",
"set_aosp_keybox": "设置 AOSP 密钥",
"set_valid_keybox": "设置有效密钥",
+ "set_custom_keybox": "设置自定义密钥",
"set_verified_boot_hash": "设置哈希值",
"about": "关于"
},
@@ -81,6 +84,9 @@
"installed": "安装完成,重启生效",
"install_fail": "安装失败,请手动更新",
"rebooting": "正在重启...",
- "reboot_fail": "重启失败,请手动重启"
+ "reboot_fail": "重启失败,请手动重启",
+ "custom_key_set": "成功设置自定义密钥",
+ "custom_key_set_error": "设置自定义密钥失败",
+ "no_file_selected": "未选择文件"
}
}
\ No newline at end of file
diff --git a/module/webui/locales/zh-TW.json b/module/webui/locales/zh-TW.json
index ff179be..cf3dabe 100644
--- a/module/webui/locales/zh-TW.json
+++ b/module/webui/locales/zh-TW.json
@@ -17,6 +17,8 @@
"deselect_unnecessary_description": "非必要分類:Xposed 模組、root 管理器、與 root 相關的應用,以及從不檢查 bootloader 狀態的通用應用。此功能需連網使用。",
"set_keybox": "設置 AOSP & 有效密鑰",
"set_keybox_description": "替換 Tricky Store 的密鑰(keybox.xml)。如果沒有有效密鑰,將替換為 AOSP 密鑰。有效密鑰選項需連網使用。",
+ "set_custom_keybox": "設置自定義密鑰",
+ "set_custom_keybox_description": "從設備存儲導入自定義密鑰。僅支持 xml 文件。",
"set_verified_boot_hash": "設置哈希值",
"set_verified_boot_hash_description": "從 Key Attestation Demo 獲取 verifiedBootHash(哈希值)。通過重置 ro.boot.vbmeta.digest 修復異常 boot 狀態。"
},
@@ -45,6 +47,7 @@
"deselect_unnecessary": "取消選擇非必要應用",
"set_aosp_keybox": "設置 AOSP 密鑰",
"set_valid_keybox": "設置有效密鑰",
+ "set_custom_keybox": "設置自定義密鑰",
"set_verified_boot_hash": "設置哈希值",
"about": "關於"
},
@@ -81,6 +84,9 @@
"installed": "安裝完成,重啟生效",
"install_fail": "安裝失敗,請手動更新",
"rebooting": "正在重啟...",
- "reboot_fail": "重啟失敗,請手動重啟"
+ "reboot_fail": "重啟失敗,請手動重啟",
+ "custom_key_set": "成功設置自定義密鑰",
+ "custom_key_set_error": "設置自定義密鑰失敗",
+ "no_file_selected": "未選擇文件"
}
}
\ No newline at end of file
diff --git a/module/webui/scripts/main.js b/module/webui/scripts/main.js
index 1890ce7..4756faf 100644
--- a/module/webui/scripts/main.js
+++ b/module/webui/scripts/main.js
@@ -19,7 +19,7 @@ export const basePath = "set-path";
export const appsWithExclamation = [];
export const appsWithQuestion = [];
const ADDITIONAL_APPS = [ "android", "com.android.vending", "com.google.android.gms", "io.github.vvb2060.keyattestation", "io.github.vvb2060.mahoshojo", "icu.nullptr.nativetest" ]; // Always keep default apps in target.txt
-const rippleClasses = ['.language-option', '.menu-button', '.menu-options li', '.search-card', '.card', '.update-card', '.link-icon', '.floating-btn', '.uninstall-container', '.boot-hash-save-button', '.boot-hash-value', '.reboot', '.install'];
+const rippleClasses = ['.language-option', '.menu-button', '.menu-options li', '.search-card', '.card', '.update-card', '.link-icon', '.floating-btn', '.uninstall-container', '.boot-hash-save-button', '.boot-hash-value', '.reboot', '.install', '.file-item'];
// Variables
let e = 0;
@@ -184,7 +184,7 @@ function hideFloatingBtn() {
}
// Function to apply ripple effect
-function applyRippleEffect() {
+export function applyRippleEffect() {
rippleClasses.forEach(selector => {
document.querySelectorAll(selector).forEach(element => {
if (element.dataset.rippleListener !== "true") {
diff --git a/module/webui/scripts/menu_option.js b/module/webui/scripts/menu_option.js
index 6946e29..655c492 100644
--- a/module/webui/scripts/menu_option.js
+++ b/module/webui/scripts/menu_option.js
@@ -1,4 +1,4 @@
-import { basePath, execCommand, showPrompt, toast } from './main.js';
+import { basePath, execCommand, showPrompt, toast, applyRippleEffect } from './main.js';
// Function to check or uncheck all app
function toggleCheckboxes(shouldCheck) {
@@ -86,7 +86,7 @@ export async function aospkb() {
}
// Function to replace valid kb
-document.getElementById("extrakb").addEventListener("click", async () => {
+document.getElementById("validkb").addEventListener("click", async () => {
setTimeout(async () => {
await execCommand(`sh ${basePath}common/get_extra.sh --kb`);
}, 100);
@@ -106,4 +106,180 @@ document.getElementById("extrakb").addEventListener("click", async () => {
await aospkb();
showPrompt("prompt.no_valid_fallback", false);
}
-});
\ No newline at end of file
+});
+
+// Add file selector dialog elements dynamically
+const fileSelector = document.createElement('div');
+fileSelector.className = 'file-selector-overlay';
+fileSelector.innerHTML = `
+
+`;
+document.body.appendChild(fileSelector);
+
+// Add styles for animations
+const style = document.createElement('style');
+style.textContent = `
+ .file-selector-overlay {
+ transition: opacity 0.3s ease;
+ opacity: 0;
+ }
+ .file-selector-overlay.visible {
+ opacity: 1;
+ }
+ .file-list {
+ transition: transform 0.3s ease, opacity 0.3s ease;
+ }
+ .file-list.switching {
+ transform: scale(0.95);
+ opacity: 0;
+ }
+`;
+document.head.appendChild(style);
+
+let currentPath = '/storage/emulated/0/Download';
+
+// Function to list files in directory
+async function listFiles(path, skipAnimation = false) {
+ const fileList = document.querySelector('.file-list');
+ if (!skipAnimation) {
+ fileList.classList.add('switching');
+ await new Promise(resolve => setTimeout(resolve, 150));
+ }
+ try {
+ const result = await execCommand(`find "${path}" -maxdepth 1 -type f -name "*.xml" -o -type d ! -name ".*" | sort`);
+ const items = result.split('\n').filter(Boolean).map(item => ({
+ path: item,
+ name: item.split('/').pop(),
+ isDirectory: !item.endsWith('.xml')
+ }));
+ fileList.innerHTML = '';
+
+ // Add back button item if not in root directory
+ if (currentPath !== '/storage/emulated/0') {
+ const backItem = document.createElement('div');
+ backItem.className = 'file-item';
+ backItem.innerHTML = `
+
+ ..
+ `;
+ backItem.addEventListener('click', async () => {
+ currentPath = currentPath.split('/').slice(0, -1).join('/');
+ if (currentPath === '') currentPath = '/storage/emulated/0';
+ const currentPathElement = document.querySelector('.current-path');
+ currentPathElement.innerHTML = currentPath.split('/').filter(Boolean).join('›');
+ currentPathElement.scrollTo({
+ left: currentPathElement.scrollWidth,
+ behavior: 'smooth'
+ });
+ await listFiles(currentPath);
+ });
+
+ fileList.appendChild(backItem);
+ }
+ items.forEach(item => {
+ if (item.path === path) return;
+ const itemElement = document.createElement('div');
+ itemElement.className = 'file-item';
+ itemElement.innerHTML = `
+
+ ${item.name}
+ `;
+ itemElement.addEventListener('click', async () => {
+ if (item.isDirectory) {
+ currentPath = item.path;
+ const currentPathElement = document.querySelector('.current-path');
+ currentPathElement.innerHTML = currentPath.split('/').filter(Boolean).join('›');
+ currentPathElement.scrollTo({
+ left: currentPathElement.scrollWidth,
+ behavior: 'smooth'
+ });
+ await listFiles(item.path);
+ } else {
+ try {
+ const content = await execCommand(`cat "${item.path}"`);
+ await execCommand(`mv -f /data/adb/tricky_store/keybox.xml /data/adb/tricky_store/keybox.xml.bak 2>/dev/null && echo '${content}' > /data/adb/tricky_store/keybox.xml`);
+ fileSelector.style.display = 'none';
+ showPrompt('prompt.custom_key_set');
+ } catch (error) {
+ console.error('Error processing file:', error);
+ showPrompt('prompt.custom_key_set_error');
+ }
+ }
+ });
+ fileList.appendChild(itemElement);
+ });
+
+ if (!skipAnimation) {
+ fileList.classList.remove('switching');
+ }
+ } catch (error) {
+ console.error('Error listing files:', error);
+ if (!skipAnimation) {
+ fileList.classList.remove('switching');
+ }
+ }
+ applyRippleEffect();
+}
+
+// Back button handler
+document.querySelector('.back-button').addEventListener('click', async () => {
+ if (currentPath === '/storage/emulated/0') return;
+ currentPath = currentPath.split('/').slice(0, -1).join('/');
+ if (currentPath === '') currentPath = '/storage/emulated/0';
+ const currentPathElement = document.querySelector('.current-path');
+ currentPathElement.innerHTML = currentPath.split('/').filter(Boolean).join('›');
+ currentPathElement.scrollTo({
+ left: currentPathElement.scrollWidth,
+ behavior: 'smooth'
+ });
+ await listFiles(currentPath);
+});
+
+// Close custom keybox selector
+document.querySelector('.close-selector').addEventListener('click', () => {
+ fileSelector.classList.remove('visible');
+ document.body.classList.remove("no-scroll");
+ setTimeout(() => {
+ fileSelector.style.display = 'none';
+ }, 300);
+});
+fileSelector.addEventListener('click', (event) => {
+ if (event.target === fileSelector) {
+ fileSelector.classList.remove('visible');
+ document.body.classList.remove("no-scroll");
+ setTimeout(() => {
+ fileSelector.style.display = 'none';
+ }, 300);
+ }
+});
+
+// Open custom keybox selector
+document.getElementById('customkb').addEventListener('click', async () => {
+ fileSelector.style.display = 'flex';
+ document.body.classList.add("no-scroll");
+ fileSelector.offsetHeight;
+ fileSelector.classList.add('visible');
+ currentPath = '/storage/emulated/0/Download';
+ const currentPathElement = document.querySelector('.current-path');
+ currentPathElement.innerHTML = currentPath.split('/').filter(Boolean).join('›');
+ currentPathElement.scrollTo({
+ left: currentPathElement.scrollWidth,
+ behavior: 'smooth'
+ });
+ await listFiles(currentPath, true);
+});
diff --git a/module/webui/styles/about.css b/module/webui/styles/about.css
index 1457ff1..20e6c40 100644
--- a/module/webui/styles/about.css
+++ b/module/webui/styles/about.css
@@ -41,6 +41,7 @@
border: none;
font-size: 18px;
color: #ccc;
+ user-select: none;
}
.link,
diff --git a/module/webui/styles/global.css b/module/webui/styles/global.css
index 52aefd7..6364fc7 100644
--- a/module/webui/styles/global.css
+++ b/module/webui/styles/global.css
@@ -113,6 +113,95 @@ body {
display: none;
}
+.file-selector-overlay {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 2000;
+ justify-content: center;
+ align-items: center;
+}
+
+.file-selector {
+ width: 90%;
+ max-width: 600px;
+ height: 80vh;
+ background-color: #fff;
+ border-radius: 15px;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.file-selector-header {
+ display: flex;
+ align-items: center;
+ padding: 10px;
+ border-bottom: 1px solid #ccc;
+}
+
+.current-path .separator {
+ color: #6E6E6E;
+ padding: 0 4px;
+}
+
+.back-button {
+ background: none;
+ border: none;
+ padding: 5px;
+ margin-right: 10px;
+ fill: #6E6E6E;
+ user-select: none;
+}
+
+.current-path {
+ flex-grow: 1;
+ font-size: 16px;
+ overflow: scroll;
+ white-space: nowrap;
+}
+
+.close-selector {
+ background: none;
+ border: none;
+ font-size: 20px;
+ color: #ccc;
+ padding: 5px;
+}
+
+.file-list {
+ flex-grow: 1;
+ overflow-y: auto;
+ padding: 10px;
+}
+
+.file-item {
+ display: flex;
+ align-items: center;
+ padding: 10px;
+ border-radius: 8px;
+ background-color: #fff;
+ position: relative;
+ overflow: hidden;
+ user-select: none;
+}
+
+.file-item svg {
+ margin-right: 10px;
+ fill: #6E6E6E;
+}
+
+.file-item span {
+ flex-grow: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
.ripple {
position: absolute;
border-radius: 50%;
@@ -138,4 +227,29 @@ body {
background-color: #121212;
color: #fff;
}
+
+ .file-selector {
+ background-color: #343434;
+ color: #fff;
+ }
+
+ .file-selector-header {
+ border-bottom: 1px solid #6E6E6E;
+ }
+
+ .file-item {
+ background-color: #343434;
+ }
+
+ .current-path .separator {
+ color: #C2C2C2;
+ }
+
+ .back-button {
+ fill: #C2C2C2;
+ }
+
+ .file-item svg {
+ fill: #C2C2C2;
+ }
}
\ No newline at end of file
diff --git a/module/webui/styles/header.css b/module/webui/styles/header.css
index d2b9c8f..7b56a36 100644
--- a/module/webui/styles/header.css
+++ b/module/webui/styles/header.css
@@ -63,7 +63,7 @@
display: flex;
flex-direction: column;
position: absolute;
- right: 0;
+ right: 5px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
z-index: 1800;
@@ -180,6 +180,7 @@
border: none;
font-size: 20px;
color: #ccc;
+ user-select: none;
}
.help-content {