diff --git a/README.md b/README.md index 1febb8eb..07ed4a79 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ If you wish to translate ReVanced Manager, we're accepting translations on [Crow ## 🛠️ Building Manager from source 1. Setup flutter environment for your [platform](https://docs.flutter.dev/get-started/install) 2. Clone the repository locally -3. Add your github token in gradle.properties like [this](https://github.com/revanced/revanced-manager/blob/docs/docs/5_building-from-source.md) +3. Add your github token in gradle.properties like [this](/docs/4_building.md) 4. Open the project in terminal 5. Run `flutter pub get` in terminal 6. Then `flutter packages pub run build_runner build --delete-conflicting-outputs` (Must be done on each git pull) diff --git a/assets/i18n/ar_SA.json b/assets/i18n/ar_SA.json index e73c5ff1..d88f4459 100644 --- a/assets/i18n/ar_SA.json +++ b/assets/i18n/ar_SA.json @@ -55,8 +55,7 @@ "widgetSubtitle": "لم يتم اختيار أي تطبيق", "noAppsLabel": "لم يتم العثور على تطبيقات", "currentVersion": "الحالي", - "recommendedVersion": "موصى به", - "anyVersion": "أي" + "recommendedVersion": "موصى به" }, "patchSelectorCard": { "widgetTitle": "اختر التعديلات", diff --git a/assets/i18n/az_AZ.json b/assets/i18n/az_AZ.json index 572a3c9e..c265ee72 100644 --- a/assets/i18n/az_AZ.json +++ b/assets/i18n/az_AZ.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Heç bir tətbiq seçilməyib", "noAppsLabel": "Heç bir tətbiq tapılmadı", "currentVersion": "Hazırkı", - "recommendedVersion": "Tövsiyyə edilən", - "anyVersion": "istənilən" + "recommendedVersion": "Tövsiyyə edilən" }, "patchSelectorCard": { "widgetTitle": "Yamaqları seçin", diff --git a/assets/i18n/bg_BG.json b/assets/i18n/bg_BG.json index a7ddb2cf..672930a8 100644 --- a/assets/i18n/bg_BG.json +++ b/assets/i18n/bg_BG.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Няма избрано приложение", "noAppsLabel": "Няма намерени приложения", "currentVersion": "Текуща", - "recommendedVersion": "Препоръчана", - "anyVersion": "всяка" + "recommendedVersion": "Препоръчана" }, "patchSelectorCard": { "widgetTitle": "Изберете модификации", diff --git a/assets/i18n/bn_BD.json b/assets/i18n/bn_BD.json index aace4c50..0469f8e3 100644 --- a/assets/i18n/bn_BD.json +++ b/assets/i18n/bn_BD.json @@ -54,8 +54,7 @@ "widgetSubtitle": "কোনো অ্যাপ্লিকেশন নির্বাচন করা হয়নি", "noAppsLabel": "কোন অ্যাপ্লিকেশন পাওয়া যায়নি", "currentVersion": "বর্তমান", - "recommendedVersion": "প্রস্তাবিত", - "anyVersion": "যেকোনো সংস্করণ" + "recommendedVersion": "প্রস্তাবিত" }, "patchSelectorCard": { "widgetTitle": "প্যাচ নির্বাচন করুন", diff --git a/assets/i18n/cs_CZ.json b/assets/i18n/cs_CZ.json index 6b41be1c..8e559dff 100644 --- a/assets/i18n/cs_CZ.json +++ b/assets/i18n/cs_CZ.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Nebyla vybrána žádná aplikace", "noAppsLabel": "Nebyly nalezeny žádné aplikace", "currentVersion": "Aktuální", - "recommendedVersion": "Doporučeno", - "anyVersion": "jakákoliv" + "recommendedVersion": "Doporučeno" }, "patchSelectorCard": { "widgetTitle": "Vybrat záplaty", diff --git a/assets/i18n/da_DK.json b/assets/i18n/da_DK.json index 96104cea..ee880acf 100644 --- a/assets/i18n/da_DK.json +++ b/assets/i18n/da_DK.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Ingen applikation valgt", "noAppsLabel": "Ingen applikationer fundet", "currentVersion": "Nuværende", - "recommendedVersion": "Anbefalet", - "anyVersion": "enhver" + "recommendedVersion": "Anbefalet" }, "patchSelectorCard": { "widgetTitle": "Vælg patches", diff --git a/assets/i18n/de_DE.json b/assets/i18n/de_DE.json index 05ccb483..a4489739 100644 --- a/assets/i18n/de_DE.json +++ b/assets/i18n/de_DE.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Keine App ausgewählt", "noAppsLabel": "Keine Apps gefunden", "currentVersion": "Aktuell", - "recommendedVersion": "Empfohlen", - "anyVersion": "beliebig" + "recommendedVersion": "Empfohlen" }, "patchSelectorCard": { "widgetTitle": "Patches auswählen", diff --git a/assets/i18n/el_GR.json b/assets/i18n/el_GR.json index 4004c404..2c82c7c3 100644 --- a/assets/i18n/el_GR.json +++ b/assets/i18n/el_GR.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Δεν έχει επιλεχθεί εφαρμογή", "noAppsLabel": "Δεν βρέθηκαν εφαρμογές", "currentVersion": "Τρέχουσα", - "recommendedVersion": "Προτεινόμενη", - "anyVersion": "οποιαδήποτε" + "recommendedVersion": "Προτεινόμενη" }, "patchSelectorCard": { "widgetTitle": "Επιλέξτε τροποποιήσεις", diff --git a/assets/i18n/en_US.json b/assets/i18n/en_US.json index ae4e5360..e49a4d61 100644 --- a/assets/i18n/en_US.json +++ b/assets/i18n/en_US.json @@ -24,11 +24,13 @@ "WIP": "Work in progress...", "noInstallations": "No patched applications installed", "installed": "Installed", + "installUpdate": "Continue to install the update?", "updateDialogTitle": "Update Manager", "updateChangelogTitle": "Changelog", "notificationTitle": "Update downloaded", "notificationText": "Tap to install the update", "downloadingMessage": "Downloading update...", + "downloadedMessage": "Update downloaded!", "installingMessage": "Installing update...", "errorDownloadMessage": "Unable to download update", "errorInstallMessage": "Unable to install update", @@ -58,9 +60,10 @@ "widgetTitleSelected": "Selected application", "widgetSubtitle": "No application selected", "noAppsLabel": "No applications found", + "notInstalled":"App not installed", "currentVersion": "Current", "suggestedVersion": "Suggested", - "anyVersion": "any" + "allVersions": "All versions" }, "patchSelectorCard": { "widgetTitle": "Select patches", diff --git a/assets/i18n/es_ES.json b/assets/i18n/es_ES.json index 294c3617..e7b07029 100644 --- a/assets/i18n/es_ES.json +++ b/assets/i18n/es_ES.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Ninguna aplicación seleccionada", "noAppsLabel": "No se encontraron aplicaciones", "currentVersion": "Versión actual", - "recommendedVersion": "Versión recomendada", - "anyVersion": "cualquiera" + "recommendedVersion": "Versión recomendada" }, "patchSelectorCard": { "widgetTitle": "Seleccionar parches", diff --git a/assets/i18n/es_MX.json b/assets/i18n/es_MX.json index 371adee2..1a4316ae 100644 --- a/assets/i18n/es_MX.json +++ b/assets/i18n/es_MX.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Ninguna aplicación seleccionada", "noAppsLabel": "No se encontraron aplicaciones", "currentVersion": "Versión actual", - "recommendedVersion": "Versión recomendada", - "anyVersion": "Cualquier versión" + "recommendedVersion": "Versión recomendada" }, "patchSelectorCard": { "widgetTitle": "Seleccionar parches", diff --git a/assets/i18n/fi_FI.json b/assets/i18n/fi_FI.json index 6accd1c5..3d6f0844 100644 --- a/assets/i18n/fi_FI.json +++ b/assets/i18n/fi_FI.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Sovellusta ei ole valittu", "noAppsLabel": "Sovelluksia ei löytynyt", "currentVersion": "Nykyinen", - "recommendedVersion": "Suositeltu", - "anyVersion": "mikä tahansa" + "recommendedVersion": "Suositeltu" }, "patchSelectorCard": { "widgetTitle": "Valitse paikkaukset", diff --git a/assets/i18n/fr_FR.json b/assets/i18n/fr_FR.json index ef00924d..50e26a6f 100644 --- a/assets/i18n/fr_FR.json +++ b/assets/i18n/fr_FR.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Aucune application sélectionnée", "noAppsLabel": "Aucune application trouvée", "currentVersion": "Version actuelle", - "recommendedVersion": "Version recommandée", - "anyVersion": "n'importe laquelle" + "recommendedVersion": "Version recommandée" }, "patchSelectorCard": { "widgetTitle": "Sélectionner les patchs", diff --git a/assets/i18n/hi_IN.json b/assets/i18n/hi_IN.json index 4a013223..f75269c9 100644 --- a/assets/i18n/hi_IN.json +++ b/assets/i18n/hi_IN.json @@ -54,8 +54,7 @@ "widgetSubtitle": "कोई ऐप्लकैशन चुना हुआ नहीं", "noAppsLabel": "कोई एप्लिकेशन नहीं मिला।", "currentVersion": "वर्तमान", - "recommendedVersion": "अनुशंसित", - "anyVersion": "कोई" + "recommendedVersion": "अनुशंसित" }, "patchSelectorCard": { "widgetTitle": "पैच चुने", diff --git a/assets/i18n/hu_HU.json b/assets/i18n/hu_HU.json index 7d4a71c1..7907ddd4 100644 --- a/assets/i18n/hu_HU.json +++ b/assets/i18n/hu_HU.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Nincs kiválasztott alkalmazás", "noAppsLabel": "Nem találhatóak alkalmazások", "currentVersion": "Jelenlegi", - "recommendedVersion": "Ajánlott", - "anyVersion": "bármely" + "recommendedVersion": "Ajánlott" }, "patchSelectorCard": { "widgetTitle": "Patchek kiválasztása", diff --git a/assets/i18n/id_ID.json b/assets/i18n/id_ID.json index 6b25540a..f5146abe 100644 --- a/assets/i18n/id_ID.json +++ b/assets/i18n/id_ID.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Tidak ada aplikasi yang dipilih", "noAppsLabel": "Aplikasi tidak ditemukan", "currentVersion": "Saat ini", - "recommendedVersion": "Rekomendasi", - "anyVersion": "semua" + "recommendedVersion": "Rekomendasi" }, "patchSelectorCard": { "widgetTitle": "Pilih Modifikasi", diff --git a/assets/i18n/it_IT.json b/assets/i18n/it_IT.json index dc80fb44..26238351 100644 --- a/assets/i18n/it_IT.json +++ b/assets/i18n/it_IT.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Nessuna applicazione selezionata", "noAppsLabel": "Nessuna applicazione trovata", "currentVersion": "Attuale", - "recommendedVersion": "Consigliata", - "anyVersion": "qualsiasi" + "recommendedVersion": "Consigliata" }, "patchSelectorCard": { "widgetTitle": "Seleziona patch", diff --git a/assets/i18n/ko_KR.json b/assets/i18n/ko_KR.json index 4e637413..b5b7bf28 100644 --- a/assets/i18n/ko_KR.json +++ b/assets/i18n/ko_KR.json @@ -54,8 +54,7 @@ "widgetSubtitle": "선택된 앱 없음", "noAppsLabel": "앱이 발견되지 않음", "currentVersion": "현재 버전", - "recommendedVersion": "권장 버전", - "anyVersion": "모든 버전" + "recommendedVersion": "권장 버전" }, "patchSelectorCard": { "widgetTitle": "패치를 선택하세요", diff --git a/assets/i18n/lt_LT.json b/assets/i18n/lt_LT.json index f4ec722b..ef64cf46 100644 --- a/assets/i18n/lt_LT.json +++ b/assets/i18n/lt_LT.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Nepasirinkta programa", "noAppsLabel": "Nerastos jokios programos", "currentVersion": "Dabartinė", - "recommendedVersion": "Rekomenduojama", - "anyVersion": "bet kokia" + "recommendedVersion": "Rekomenduojama" }, "patchSelectorCard": { "widgetTitle": "Pasirinkite modifikacijas", diff --git a/assets/i18n/ms_MY.json b/assets/i18n/ms_MY.json index 0aec96b7..c653b115 100644 --- a/assets/i18n/ms_MY.json +++ b/assets/i18n/ms_MY.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Tiada aplikasi dipilih", "noAppsLabel": "Tiada aplikasi ditemui", "currentVersion": "Semasa", - "recommendedVersion": "Disyorkan", - "anyVersion": "mana-mana" + "recommendedVersion": "Disyorkan" }, "patchSelectorCard": { "widgetTitle": "Pilih modifikasi", diff --git a/assets/i18n/pl_PL.json b/assets/i18n/pl_PL.json index c60b964c..c41298db 100644 --- a/assets/i18n/pl_PL.json +++ b/assets/i18n/pl_PL.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Nie wybrano aplikacji", "noAppsLabel": "Nie znaleziono aplikacji", "currentVersion": "Aktualna", - "recommendedVersion": "Zalecana", - "anyVersion": "dowolna" + "recommendedVersion": "Zalecana" }, "patchSelectorCard": { "widgetTitle": "Wybierz łatki", diff --git a/assets/i18n/pt_BR.json b/assets/i18n/pt_BR.json index 46551a0d..24610635 100644 --- a/assets/i18n/pt_BR.json +++ b/assets/i18n/pt_BR.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Nenhum aplicativo selecionado", "noAppsLabel": "Nenhum app foi encontrado", "currentVersion": "Atual", - "recommendedVersion": "Recomendado", - "anyVersion": "todos" + "recommendedVersion": "Recomendado" }, "patchSelectorCard": { "widgetTitle": "Selecionar patches", diff --git a/assets/i18n/pt_PT.json b/assets/i18n/pt_PT.json index 94ab943b..14bc6c18 100644 --- a/assets/i18n/pt_PT.json +++ b/assets/i18n/pt_PT.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Nenhuma aplicação selecionada", "noAppsLabel": "Não foram encontradas aplicações", "currentVersion": "Atual", - "recommendedVersion": "Recomendada", - "anyVersion": "qualquer" + "recommendedVersion": "Recomendada" }, "patchSelectorCard": { "widgetTitle": "Selecionar modificações", diff --git a/assets/i18n/ro_RO.json b/assets/i18n/ro_RO.json index 308a78e2..5c972e88 100644 --- a/assets/i18n/ro_RO.json +++ b/assets/i18n/ro_RO.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Nicio aplicație selectată", "noAppsLabel": "Nu s-au găsit aplicații", "currentVersion": "Actual", - "recommendedVersion": "Recomandat", - "anyVersion": "oricare" + "recommendedVersion": "Recomandat" }, "patchSelectorCard": { "widgetTitle": "Selectați patch-urile", diff --git a/assets/i18n/ru_RU.json b/assets/i18n/ru_RU.json index 0a5d5367..736e7ade 100644 --- a/assets/i18n/ru_RU.json +++ b/assets/i18n/ru_RU.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Не выбрано приложение", "noAppsLabel": "Приложения не найдены", "currentVersion": "Текущая", - "recommendedVersion": "Рекомендуемая", - "anyVersion": "любая" + "recommendedVersion": "Рекомендуемая" }, "patchSelectorCard": { "widgetTitle": "Выберите патчи", diff --git a/assets/i18n/sk_SK.json b/assets/i18n/sk_SK.json index f6408391..4f0f8b19 100644 --- a/assets/i18n/sk_SK.json +++ b/assets/i18n/sk_SK.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Nie je vybraná žiadna aplikácia", "noAppsLabel": "Nebola nájdená žiadna aplikácia", "currentVersion": "Aktuálne", - "recommendedVersion": "Odporúčané", - "anyVersion": "akákoľvek" + "recommendedVersion": "Odporúčané" }, "patchSelectorCard": { "widgetTitle": "Vyberte záplaty", diff --git a/assets/i18n/sr_CS.json b/assets/i18n/sr_CS.json index 7c0cf5a6..9a4d4179 100644 --- a/assets/i18n/sr_CS.json +++ b/assets/i18n/sr_CS.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Nijedna aplikacija nije izabrana", "noAppsLabel": "Nijedna aplikacija nije pronađena", "currentVersion": "Trenutna", - "recommendedVersion": "Preporučena", - "anyVersion": "bilo koja" + "recommendedVersion": "Preporučena" }, "patchSelectorCard": { "widgetTitle": "Izaberite pečeve", diff --git a/assets/i18n/sr_SP.json b/assets/i18n/sr_SP.json index e012e6a8..8e4df5a9 100644 --- a/assets/i18n/sr_SP.json +++ b/assets/i18n/sr_SP.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Ниједна апликација није изабрана", "noAppsLabel": "Ниједна апликација није пронађена", "currentVersion": "Тренутна", - "recommendedVersion": "Препоручена", - "anyVersion": "било која" + "recommendedVersion": "Препоручена" }, "patchSelectorCard": { "widgetTitle": "Изаберите печеве", diff --git a/assets/i18n/sv_SE.json b/assets/i18n/sv_SE.json index e1b6d52a..58dd789a 100644 --- a/assets/i18n/sv_SE.json +++ b/assets/i18n/sv_SE.json @@ -54,8 +54,7 @@ "widgetSubtitle": "Ingen app vald", "noAppsLabel": "Inga applikationer hittades", "currentVersion": "Nuvarande", - "recommendedVersion": "Rekommenderad", - "anyVersion": "alla" + "recommendedVersion": "Rekommenderad" }, "patchSelectorCard": { "widgetTitle": "Välj patchar", diff --git a/assets/i18n/sw_KE.json b/assets/i18n/sw_KE.json index 794e7bdc..08a60a02 100644 --- a/assets/i18n/sw_KE.json +++ b/assets/i18n/sw_KE.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Hakuna programu iliyochaguliwa", "noAppsLabel": "Hakuna programu zilizopatikana", "currentVersion": "Sasa", - "recommendedVersion": "Imependekezwa", - "anyVersion": "yoyote" + "recommendedVersion": "Imependekezwa" }, "patchSelectorCard": { "widgetTitle": "Chagua viraka", diff --git a/assets/i18n/tr_TR.json b/assets/i18n/tr_TR.json index b4b0253d..9a5c254d 100644 --- a/assets/i18n/tr_TR.json +++ b/assets/i18n/tr_TR.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Hiçbir uygulama seçilmedi", "noAppsLabel": "Hiçbir uygulama bulunamadı", "currentVersion": "Şu anki", - "recommendedVersion": "Önerilen", - "anyVersion": "herhangi" + "recommendedVersion": "Önerilen" }, "patchSelectorCard": { "widgetTitle": "Yamaları Seçin", diff --git a/assets/i18n/ur_PK.json b/assets/i18n/ur_PK.json index 1dbfdf91..91c6e30d 100644 --- a/assets/i18n/ur_PK.json +++ b/assets/i18n/ur_PK.json @@ -54,8 +54,7 @@ "widgetSubtitle": "کوئی ایپلیکیشن منتخب نہیں", "noAppsLabel": "کوئی ایپلیکیشن نہیں ملی", "currentVersion": "موجودہ", - "recommendedVersion": "تجویز کردہ", - "anyVersion": "کوئی بھی" + "recommendedVersion": "تجویز کردہ" }, "patchSelectorCard": { "widgetTitle": "پیچز منتخب کریں", diff --git a/assets/i18n/vi_VN.json b/assets/i18n/vi_VN.json index 7d06b24f..875eefb2 100644 --- a/assets/i18n/vi_VN.json +++ b/assets/i18n/vi_VN.json @@ -55,8 +55,7 @@ "widgetSubtitle": "Chưa chọn ứng dụng nào", "noAppsLabel": "Không tìm thấy ứng dụng nào", "currentVersion": "Hiện tại", - "recommendedVersion": "Đề xuất", - "anyVersion": "bất kỳ" + "recommendedVersion": "Đề xuất" }, "patchSelectorCard": { "widgetTitle": "Chọn bản vá", diff --git a/assets/i18n/zh_CN.json b/assets/i18n/zh_CN.json index e64c2c02..a76e2571 100644 --- a/assets/i18n/zh_CN.json +++ b/assets/i18n/zh_CN.json @@ -54,8 +54,7 @@ "widgetSubtitle": "未选择应用程序", "noAppsLabel": "未发现应用程序。", "currentVersion": "当前", - "recommendedVersion": "推荐", - "anyVersion": "任意" + "recommendedVersion": "推荐" }, "patchSelectorCard": { "widgetTitle": "选择补丁", diff --git a/assets/i18n/zh_HK.json b/assets/i18n/zh_HK.json index 37dd6ec1..817ab21d 100644 --- a/assets/i18n/zh_HK.json +++ b/assets/i18n/zh_HK.json @@ -54,8 +54,7 @@ "widgetSubtitle": "沒有選擇應用程式", "noAppsLabel": "找不到應用程式", "currentVersion": "目前版本", - "recommendedVersion": "推薦版本", - "anyVersion": "任何版本" + "recommendedVersion": "推薦版本" }, "patchSelectorCard": { "widgetTitle": "選擇補丁", diff --git a/assets/i18n/zh_TW.json b/assets/i18n/zh_TW.json index 96594ab5..262e162d 100644 --- a/assets/i18n/zh_TW.json +++ b/assets/i18n/zh_TW.json @@ -55,8 +55,7 @@ "widgetSubtitle": "未選取應用程式", "noAppsLabel": "找不到應用程式", "currentVersion": "目前", - "recommendedVersion": "建議", - "anyVersion": "任何" + "recommendedVersion": "建議" }, "patchSelectorCard": { "widgetTitle": "選取修補檔", diff --git a/docs/0_prerequisites.md b/docs/0_prerequisites.md index 5a4b50c3..a53b46fb 100644 --- a/docs/0_prerequisites.md +++ b/docs/0_prerequisites.md @@ -11,6 +11,6 @@ In order to use ReVanced Manager, certain requirements must be met. ## ⏭️ What's next -The next page will guide you through patching an application. +The next page will guide you through patching an app. Continue: [⬇️ Installation](1_installation.md) diff --git a/docs/1_installation.md b/docs/1_installation.md index 79443658..45898582 100644 --- a/docs/1_installation.md +++ b/docs/1_installation.md @@ -4,11 +4,11 @@ In order to use ReVanced on your Android device, ReVanced Manager must be instal ## 🪜 Installation steps -- Download the latest version of ReVanced Manager from [here](https://github.com/revanced/revanced-manager/releases/latest) -- Install ReVanced Manager +1. Download the latest version of ReVanced Manager from [here](https://github.com/revanced/revanced-manager/releases/latest) +2. Install ReVanced Manager ## ⏭️ What's next -The next page will guide you through patching an application. +The next page will guide you through patching an app. Continue: [🪛 Usage](2_usage.md) diff --git a/docs/2_1_patching.md b/docs/2_1_patching.md index 7edc7d87..5dab9d64 100644 --- a/docs/2_1_patching.md +++ b/docs/2_1_patching.md @@ -1,26 +1,24 @@ -# 🧩 Patching applications +# 🧩 Patching apps -The following pages will guide you through using ReVanced Manager to patch applications. +The following pages will guide you through using ReVanced Manager to patch apps. -## 🪜 Steps to patch applications +## 🪜 Steps to patch apps 1. Navigate to the **Patcher** tab from the bottom navigation bar -2. Tap on the **Select an application** card -3. Choose an application to patch[^1] - > **Note**: The application version suggested by ReVanced Manager is visible in each application's card. This is important in step 5. below, as most patches are compatible with this version. +2. Tap on the **Select an app** card +3. Choose an app to patch[^1] + > **Note**: The suggested version is visible in each app's card. 4. Tap on the **Select patches** card and select the patches you want to apply[^2] - > **Warning**: If you see a warning icon next to a patch, it means that the patch is not compatible with the application version you selected in step 3. above. In this case, you should either go back and select a different application version or deselect the patch. -5. Tap on the **Done** button to confirm your selection -6. Tap on the **Patch** button to start patching - > **Warning**: The patching process can take anywhere from 2 to 5 minutes depending on your device. Refrain from exiting ReVanced Manager while the process is still running, otherwise the patching time will increase significantly. -7. After patching is complete, tap on the **Install** button to install the patched application - > **Note**: If you have root access on your device, you may have the option to mount the patched application on top of the original application.[^4] - > Optionally, you may tap on the three vertical dots in the top right to export the patched application to storage. + > **Warning**: If you see a warning you can click on it for more information. +5. Tap on the **Done** then **Patch** button + > **Warning**: The patching process may take ~5 minutes. Exiting the app may increase the time it takes to patch. +6. Tap on the **Install** button + > **Note**: If you are rooted, you can mount the patched app on top of the original app.[^3] + > Optionally, you may export the patched app to storage using the options in the top right corner. -[^1]: You may be prompted to select an application from storage in case you have no root access on your device. In this case, select the APK file from your device storage.[^3] -[^2]: It is suggested to use the default set of patches by tapping on the **Default** chip above the list of patches. -[^3]: You can obtain `.apk` files from sites such as [APKMirror](https://www.apkmirror.com/). -[^4]: Mounting the patched application on top of the original application will only work if the installed application version matches the version of the application selected in step 3. above. +[^1]: Non-root users may be prompted to select an APK from storage, in which case you have to source the APK file yourself. ReVanced does not provide any APK files. +[^2]: It is suggested to use the default set of patches by tapping on the **Default** button above the list of patches. +[^3]: Mounting the patched app on top of the original app will only work if the installed app version matches the version of the app selected in step 3. above. ## ⏭️ What's next diff --git a/docs/2_2_managing.md b/docs/2_2_managing.md index a4e9bd17..1a04e220 100644 --- a/docs/2_2_managing.md +++ b/docs/2_2_managing.md @@ -1,12 +1,12 @@ -# 🧰 Managing patched applications +# 🧰 Managing patched apps -After patching an application, you may want to manage it. This page will guide you through managing patched applications. +After patching an app, you may want to manage it. This page will guide you through managing patched apps. -## 🪜 Steps to manage patched applications +## 🪜 Steps to manage patched apps 1. Tap on the **Dashboard** tab in the bottom navigation bar 2. Select the **Installed** chip -3. Tap on the **Info** button for the application you want to manage +3. Tap on the **Info** button for the app you want to manage 4. Choose one of the options from the menu ## ⏭️ What's next diff --git a/docs/2_4_settings.md b/docs/2_4_settings.md index fe57ce01..e1d49033 100644 --- a/docs/2_4_settings.md +++ b/docs/2_4_settings.md @@ -14,15 +14,15 @@ ReVanced Manager has settings that can be configured to your liking. - ### 🧪 Experimental ReVanced Patches support - Lift application version constraints from ReVanced Patches. This allows you to patch any version of an application, even if the patch is not explicitly compatible with it. + Lift app version constraints from ReVanced Patches. This allows you to patch any version of an app, even if the patch is not explicitly compatible with it. - ### 🧑‍🔬 Experimental universal support - This will show or hide ReVanced Patches, which are not meant for any application in particular but rather for all applications but may not work on all applications. + This will show or hide ReVanced Patches, which are not meant for any app in particular but rather for all apps but may not work on all apps. - ### 🔑 Export, import or delete keystore - Manage the keystore used to sign patched applications. + Manage the keystore used to sign patched apps. - ### 📄 Export, import or reset ReVanced Patches selection diff --git a/docs/2_usage.md b/docs/2_usage.md index f496eea9..2bef4330 100644 --- a/docs/2_usage.md +++ b/docs/2_usage.md @@ -1,11 +1,11 @@ # 🛠️ Usage -The following pages will guide you through using ReVanced Manager to patch applications, manage patched applications, and update ReVanced Manager. +The following pages will guide you through using ReVanced Manager to patch apps, manage patched apps, and update ReVanced Manager. ## 📖 Table of contents -1. [🧩 Patching applications](2_1_patching.md) -2. [🧰 Managing patched applications](2_2_managing.md) +1. [🧩 Patching apps](2_1_patching.md) +2. [🧰 Managing patched apps](2_2_managing.md) 3. [🔄 Updating ReVanced Manager](2_3_updating.md) 4. [⚙️ Configuring ReVanced Manager](2_4_settings.md) diff --git a/docs/3_troubleshooting.md b/docs/3_troubleshooting.md index d68f317d..80b93048 100644 --- a/docs/3_troubleshooting.md +++ b/docs/3_troubleshooting.md @@ -2,23 +2,27 @@ In case you encounter any issues while using ReVanced Manager, please refer to this page for possible solutions. -- ### 💥 App not installed as package conflicts with an existing package +- 💉 Patching fails with an error - An existing installation of the application you're trying to patch is conflicting with the patched application. Uninstall the existing application before installing the patched application. + Make sure ReVanced Manager is up to date by following [🔄 Updating ReVanced Manager](2_3_updating.md) and select the **Default** button when choosing patches. + +- 💥 App not installed as package conflicts with an existing package -- ### ❗️Error code `135`, `139` or `1` when patching the application + An existing installation of the app you're trying to patch is conflicting with the patched app. Uninstall the existing app before installing the patched app. + +- ❗️ Error code `135`, `139` or `1` when patching the app Your device is not supported. Refer to the [Prerequisites](0_prerequisites.md) page for supported devices. - Alternatively, you can use [ReVanced CLI](https://github.com/revanced/revanced-cli) to patch the application. + Alternatively, you can use [ReVanced CLI](https://github.com/revanced/revanced-cli) to patch the app. -- ### 🚫 Non-root install is not possible with the current patches selection +- 🚫 Non-root install is not possible with the current patches selection - Select the **Default** chip when choosing patches. + Select the **Default** button when choosing patches. -- ### 🚨 Patched application crashes on launch +- 🚨 Patched app crashes on launch - Select the **Default** chip when choosing patches. + Select the **Default** button when choosing patches. ## ⏭️ What's next diff --git a/docs/README.md b/docs/README.md index 5fa36d3f..0577ad03 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,9 +7,15 @@ This documentation explains how to use [ReVanced Manager](https://github.com/rev 0. [💼 Prerequisites](0_prerequisites.md) 1. [⬇️ Installation](1_installation.md) 2. [🛠️ Usage](2_usage.md) - 1. [🧩 Patching applications](2_1_patching.md) - 2. [🧰 Managing patched applications](2_2_managing.md) + 1. [🧩 Patching apps](2_1_patching.md) + 2. [🧰 Managing patched apps](2_2_managing.md) 3. [🔄 Updating ReVanced Manager](2_3_updating.md) 4. [⚙️ Configuring ReVanced Manager](2_4_settings.md) 3. [🛟 Troubleshooting](3_troubleshooting.md) 4. [🛠 Building from source](4_building.md) + +## ⏭️ Start here + +The next page will tell you about the prerequisites for using ReVanced Manager. + +Continue: [💼 Prerequisites](0_prerequisites.md) diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index 026fdb5c..cf6e6b97 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -144,30 +144,23 @@ class PatcherAPI { ); } - Future getOriginalFilePath( - String packageName, - String originalFilePath, - ) async { + Future getOriginalFilePath(String packageName) async { try { final bool hasRootPermissions = await _rootAPI.hasRootPermissions(); if (hasRootPermissions) { - originalFilePath = await _rootAPI.getOriginalFilePath( - packageName, - originalFilePath, - ); + return await _rootAPI.getOriginalFilePath(packageName); } - return originalFilePath; } on Exception catch (e) { if (kDebugMode) { print(e); } - return originalFilePath; } + return ''; } Future runPatcher( String packageName, - String originalFilePath, + String apkFilePath, List selectedPatches, ) async { final bool includeSettings = await needsSettingsPatch(selectedPatches); @@ -198,15 +191,16 @@ class PatcherAPI { _outFile = File('${workDir.path}/out.apk'); final Directory cacheDir = Directory('${workDir.path}/cache'); cacheDir.createSync(); + String originalFilePath = await getOriginalFilePath(packageName); + if (originalFilePath.isEmpty) { + originalFilePath = apkFilePath; + } try { await patcherChannel.invokeMethod( 'runPatcher', { 'patchBundleFilePath': patchBundleFile.path, - 'originalFilePath': await getOriginalFilePath( - packageName, - originalFilePath, - ), + 'originalFilePath': originalFilePath, 'inputFilePath': inputFile.path, 'patchedFilePath': patchedFile.path, 'outFilePath': _outFile!.path, diff --git a/lib/services/revanced_api.dart b/lib/services/revanced_api.dart index 11fa084c..a8bec19c 100644 --- a/lib/services/revanced_api.dart +++ b/lib/services/revanced_api.dart @@ -140,7 +140,7 @@ class RevancedAPI { return null; } - StreamController managerUpdateProgress = StreamController(); + StreamController managerUpdateProgress = StreamController.broadcast(); void updateManagerDownloadProgress(int progress) { managerUpdateProgress.add(progress.toDouble()); @@ -170,6 +170,7 @@ class RevancedAPI { updateManagerDownloadProgress(progress); } else if (result is FileInfo) { + disposeManagerUpdateProgress(); // The download is complete; convert the FileInfo object to a File object outputFile = File(result.file.path); } diff --git a/lib/services/root_api.dart b/lib/services/root_api.dart index 727bac1d..af1276c4 100644 --- a/lib/services/root_api.dart +++ b/lib/services/root_api.dart @@ -2,7 +2,9 @@ import 'package:flutter/foundation.dart'; import 'package:root/root.dart'; class RootAPI { - final String _managerDirPath = '/data/local/tmp/revanced-manager'; + // TODO(ponces): remove in the future, keep it for now during migration. + final String _revancedOldDirPath = '/data/local/tmp/revanced-manager'; + final String _revancedDirPath = '/data/adb/revanced'; final String _postFsDataDirPath = '/data/adb/post-fs-data.d'; final String _serviceDDirPath = '/data/adb/service.d'; @@ -40,55 +42,62 @@ class RootAPI { seLinux, String filePath, ) async { - if (permissions.isNotEmpty) { - await Root.exec( - cmd: 'chmod $permissions "$filePath"', - ); - } - if (ownerGroup.isNotEmpty) { - await Root.exec( - cmd: 'chown $ownerGroup "$filePath"', - ); - } - if (seLinux.isNotEmpty) { - await Root.exec( - cmd: 'chcon $seLinux "$filePath"', - ); - } - } - - Future isAppInstalled(String packageName) async { - if (packageName.isNotEmpty) { - String? res = await Root.exec( - cmd: 'ls "$_managerDirPath/$packageName"', - ); - if (res != null && res.isNotEmpty) { - res = await Root.exec( - cmd: 'ls "$_serviceDDirPath/$packageName.sh"', - ); - return res != null && res.isNotEmpty; - } - } - return false; - } - - Future> getInstalledApps() async { try { - final String? res = await Root.exec( - cmd: 'ls "$_managerDirPath"', - ); - if (res != null) { - final List apps = res.split('\n'); - apps.removeWhere((pack) => pack.isEmpty); - return apps.map((pack) => pack.trim()).toList(); + if (permissions.isNotEmpty) { + await Root.exec( + cmd: 'chmod $permissions "$filePath"', + ); + } + if (ownerGroup.isNotEmpty) { + await Root.exec( + cmd: 'chown $ownerGroup "$filePath"', + ); + } + if (seLinux.isNotEmpty) { + await Root.exec( + cmd: 'chcon $seLinux "$filePath"', + ); } } on Exception catch (e) { if (kDebugMode) { print(e); } - return List.empty(); } - return List.empty(); + } + + Future isAppInstalled(String packageName) async { + if (packageName.isNotEmpty) { + return fileExists('$_serviceDDirPath/$packageName.sh'); + } + return false; + } + + Future> getInstalledApps() async { + final List apps = List.empty(); + try { + String? res = await Root.exec( + cmd: 'ls "$_revancedDirPath"', + ); + if (res != null) { + final List list = res.split('\n'); + list.removeWhere((pack) => pack.isEmpty); + apps.addAll(list.map((pack) => pack.trim()).toList()); + } + // TODO(ponces): remove in the future, keep it for now during migration. + res = await Root.exec( + cmd: 'ls "$_revancedOldDirPath"', + ); + if (res != null) { + final List list = res.split('\n'); + list.removeWhere((pack) => pack.isEmpty); + apps.addAll(list.map((pack) => pack.trim()).toList()); + } + } on Exception catch (e) { + if (kDebugMode) { + print(e); + } + } + return apps; } Future deleteApp(String packageName, String originalFilePath) async { @@ -98,8 +107,12 @@ class RootAPI { await Root.exec( cmd: 'su -mm -c "umount -l $originalFilePath"', ); + // TODO(ponces): remove in the future, keep it for now during migration. await Root.exec( - cmd: 'rm -rf "$_managerDirPath/$packageName"', + cmd: 'rm -rf "$_revancedOldDirPath/$packageName"', + ); + await Root.exec( + cmd: 'rm -rf "$_revancedDirPath/$packageName"', ); await Root.exec( cmd: 'rm -rf "$_serviceDDirPath/$packageName.sh"', @@ -117,13 +130,13 @@ class RootAPI { try { await deleteApp(packageName, originalFilePath); await Root.exec( - cmd: 'mkdir -p "$_managerDirPath/$packageName"', + cmd: 'mkdir -p "$_revancedDirPath/$packageName"', ); await setPermissions( '0755', 'shell:shell', '', - '$_managerDirPath/$packageName', + '$_revancedDirPath/$packageName', ); await saveOriginalFilePath(packageName, originalFilePath); await installServiceDScript(packageName); @@ -142,7 +155,7 @@ class RootAPI { Future installServiceDScript(String packageName) async { final String content = '#!/system/bin/sh\n' 'while [ "\$(getprop sys.boot_completed | tr -d \'"\'"\'\\\\r\'"\'"\')" != "1" ]; do sleep 3; done\n' - 'base_path=$_managerDirPath/$packageName/base.apk\n' + 'base_path=$_revancedDirPath/$packageName/base.apk\n' 'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n' r'[ ! -z $stock_path ] && mount -o bind $base_path $stock_path'; final String scriptFilePath = '$_serviceDDirPath/$packageName.sh'; @@ -164,7 +177,7 @@ class RootAPI { } Future installApk(String packageName, String patchedFilePath) async { - final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk'; + final String newPatchedFilePath = '$_revancedDirPath/$packageName/base.apk'; await Root.exec( cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"', ); @@ -177,7 +190,7 @@ class RootAPI { } Future mountApk(String packageName, String originalFilePath) async { - final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk'; + final String newPatchedFilePath = '$_revancedDirPath/$packageName/base.apk'; await Root.exec( cmd: 'am force-stop "$packageName"', ); @@ -196,21 +209,18 @@ class RootAPI { return res != null && res.isNotEmpty; } - Future getOriginalFilePath( - String packageName, - String originalFilePath, - ) async { + Future getOriginalFilePath(String packageName) async { + final String originalPath = '$_revancedDirPath/$packageName/original.apk'; + final String oldOrigPath = '$_revancedOldDirPath/$packageName/original.apk'; final bool isInstalled = await isAppInstalled(packageName); if (isInstalled && await isMounted(packageName)) { - originalFilePath = '$_managerDirPath/$packageName/original.apk'; - await setPermissions( - '0644', - 'shell:shell', - 'u:object_r:apk_data_file:s0', - originalFilePath, - ); + if (await fileExists(originalPath)) { + return originalPath; + } else if (await fileExists(oldOrigPath)) { + return oldOrigPath; + } } - return originalFilePath; + return ''; } Future saveOriginalFilePath( @@ -218,15 +228,15 @@ class RootAPI { String originalFilePath, ) async { final String originalRootPath = - '$_managerDirPath/$packageName/original.apk'; + '$_revancedDirPath/$packageName/original.apk'; await Root.exec( - cmd: 'mkdir -p "$_managerDirPath/$packageName"', + cmd: 'mkdir -p "$_revancedDirPath/$packageName"', ); await setPermissions( '0755', 'shell:shell', '', - '$_managerDirPath/$packageName', + '$_revancedDirPath/$packageName', ); await Root.exec( cmd: 'cp "$originalFilePath" "$originalRootPath"', @@ -238,4 +248,18 @@ class RootAPI { originalFilePath, ); } + + Future fileExists(String path) async { + try { + final String? res = await Root.exec( + cmd: 'ls $path', + ); + return res != null && res.isNotEmpty; + } on Exception catch (e) { + if (kDebugMode) { + print(e); + } + return false; + } + } } diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart index edcc357d..b05f0e4d 100644 --- a/lib/ui/views/home/home_viewmodel.dart +++ b/lib/ui/views/home/home_viewmodel.dart @@ -1,7 +1,6 @@ // ignore_for_file: use_build_context_synchronously import 'dart:async'; import 'dart:io'; - import 'package:app_installer/app_installer.dart'; import 'package:cross_connectivity/cross_connectivity.dart'; import 'package:flutter/foundation.dart'; @@ -39,6 +38,7 @@ class HomeViewModel extends BaseViewModel { List patchedInstalledApps = []; List patchedUpdatableApps = []; String? _latestManagerVersion = ''; + File? downloadedApk; Future initialize(BuildContext context) async { _latestManagerVersion = await _managerAPI.getLatestManagerVersion(); @@ -162,74 +162,134 @@ class HomeViewModel extends BaseViewModel { } Future updateManager(BuildContext context) async { + final ValueNotifier downloaded = ValueNotifier(false); try { _toast.showBottom('homeView.downloadingMessage'); showDialog( context: context, - builder: (context) => SimpleDialog( - contentPadding: const EdgeInsets.all(16.0), - title: I18nText( - 'homeView.downloadingMessage', - child: Text( - '', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w500, - color: Theme.of(context).colorScheme.secondary, - ), - ), - ), - children: [ - Column( - children: [ - Row( - children: [ - Icon( - Icons.new_releases_outlined, - color: Theme.of(context).colorScheme.secondary, - ), - const SizedBox(width: 8.0), - Text( - '$_latestManagerVersion', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Theme.of(context).colorScheme.secondary, - ), - ), - ], - ), - const SizedBox(height: 16.0), - StreamBuilder( - initialData: 0.0, - stream: _revancedAPI.managerUpdateProgress.stream, - builder: (context, snapshot) { - return LinearProgressIndicator( - value: snapshot.data! * 0.01, - valueColor: AlwaysStoppedAnimation( - Theme.of(context).colorScheme.secondary, - ), - ); - }, - ), - const SizedBox(height: 16.0), - Align( - alignment: Alignment.centerRight, - child: CustomMaterialButton( - label: I18nText('cancelButton'), - onPressed: () { - _revancedAPI.disposeManagerUpdateProgress(); - Navigator.of(context).pop(); - }, + builder: (context) => ValueListenableBuilder( + valueListenable: downloaded, + builder: (context, value, child) { + return SimpleDialog( + contentPadding: const EdgeInsets.all(16.0), + title: I18nText( + !value + ? 'homeView.downloadingMessage' + : 'homeView.downloadedMessage', + child: Text( + '', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.secondary, ), ), + ), + children: [ + Column( + children: [ + Row( + children: [ + Icon( + Icons.new_releases_outlined, + color: Theme.of(context).colorScheme.secondary, + ), + const SizedBox(width: 8.0), + Text( + '$_latestManagerVersion', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.secondary, + ), + ), + ], + ), + const SizedBox(height: 16.0), + if (!value) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + StreamBuilder( + initialData: 0.0, + stream: _revancedAPI.managerUpdateProgress.stream, + builder: (context, snapshot) { + return LinearProgressIndicator( + value: snapshot.data! * 0.01, + valueColor: AlwaysStoppedAnimation( + Theme.of(context).colorScheme.secondary, + ), + ); + }, + ), + const SizedBox(height: 16.0), + Align( + alignment: Alignment.centerRight, + child: CustomMaterialButton( + label: I18nText('cancelButton'), + onPressed: () { + _revancedAPI.disposeManagerUpdateProgress(); + Navigator.of(context).pop(); + }, + ), + ), + ], + ), + if (value) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + I18nText( + 'homeView.installUpdate', + child: Text( + '', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + const SizedBox(height: 16.0), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Align( + alignment: Alignment.centerRight, + child: CustomMaterialButton( + isFilled: false, + label: I18nText('cancelButton'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + const SizedBox(width: 8.0), + Align( + alignment: Alignment.centerRight, + child: CustomMaterialButton( + label: I18nText('updateButton'), + onPressed: () async { + await AppInstaller.installApk( + downloadedApk!.path,); + }, + ), + ), + ], + ), + ], + ), + ], + ), ], - ), - ], + ); + }, ), ); final File? managerApk = await downloadManager(); if (managerApk != null) { + downloaded.value = true; + downloadedApk = managerApk; // await flutterLocalNotificationsPlugin.zonedSchedule( // 0, // FlutterI18n.translate( diff --git a/lib/ui/views/patcher/patcher_viewmodel.dart b/lib/ui/views/patcher/patcher_viewmodel.dart index f828f947..f97f1310 100644 --- a/lib/ui/views/patcher/patcher_viewmodel.dart +++ b/lib/ui/views/patcher/patcher_viewmodel.dart @@ -133,7 +133,7 @@ class PatcherViewModel extends BaseViewModel { if (suggestedVersion.isEmpty) { suggestedVersion = FlutterI18n.translate( context, - 'appSelectorCard.anyVersion', + 'appSelectorCard.allVersions', ); } else { suggestedVersion = 'v$suggestedVersion'; diff --git a/lib/ui/widgets/appSelectorView/app_skeleton_loader.dart b/lib/ui/widgets/appSelectorView/app_skeleton_loader.dart index 0c7ee017..c1231b59 100644 --- a/lib/ui/widgets/appSelectorView/app_skeleton_loader.dart +++ b/lib/ui/widgets/appSelectorView/app_skeleton_loader.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:skeletons/skeletons.dart'; class AppSkeletonLoader extends StatelessWidget { @@ -7,59 +8,64 @@ class AppSkeletonLoader extends StatelessWidget { @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; - return Skeleton( - isLoading: true, - skeleton: ListView.builder( - shrinkWrap: true, - itemCount: 7, - itemBuilder: (context, index) => Padding( - padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8.0), - child: SkeletonItem( - child: Row( - children: [ - SkeletonAvatar( - style: SkeletonAvatarStyle( - width: screenWidth * 0.15, - height: screenWidth * 0.15, - borderRadius: const BorderRadius.all(Radius.circular(12)), + return ListView.builder( + shrinkWrap: true, + itemCount: 7, + padding: EdgeInsets.zero, + itemBuilder: (context, index) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12.0), + child: CustomCard( + child: Row( + children: [ + SkeletonAvatar( + style: SkeletonAvatarStyle( + width: screenWidth * 0.10, + height: screenWidth * 0.10, + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + ), + const SizedBox(width: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: screenWidth * 0.4, + child: SkeletonLine( + style: SkeletonLineStyle( + height: 20, + width: screenWidth * 0.4, + borderRadius: const BorderRadius.all(Radius.circular(10)), + ), + ), ), - ), - const SizedBox(width: 16), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - color: Colors.white, - height: 34, - width: screenWidth * 0.4, - child: SkeletonParagraph( - style: const SkeletonParagraphStyle( - lines: 1, - ), + const SizedBox(height: 12), + SizedBox( + width: screenWidth * 0.6, + child: SkeletonLine( + style: SkeletonLineStyle( + height: 15, + width: screenWidth * 0.6, + borderRadius: const BorderRadius.all(Radius.circular(10)), ), ), - const SizedBox(height: 12), - Container( - margin: const EdgeInsets.only(bottom: 4), - color: Colors.white, - height: 34, - width: screenWidth * 0.6, - child: SkeletonParagraph( - style: const SkeletonParagraphStyle( - lines: 1, - ), + ), + const SizedBox(height: 5), + SizedBox( + width: screenWidth * 0.5, + child: SkeletonLine( + style: SkeletonLineStyle( + height: 15, + width: screenWidth * 0.5, + borderRadius: const BorderRadius.all(Radius.circular(10)), ), ), - ], - ), - ], - ), + ), + ], + ), + ], ), ), ), - child: const Center( - child: Text('Content'), - ), ); } } diff --git a/lib/ui/widgets/appSelectorView/installed_app_item.dart b/lib/ui/widgets/appSelectorView/installed_app_item.dart index 59226e05..fc1a97e1 100644 --- a/lib/ui/widgets/appSelectorView/installed_app_item.dart +++ b/lib/ui/widgets/appSelectorView/installed_app_item.dart @@ -1,5 +1,4 @@ import 'dart:typed_data'; - import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; @@ -52,38 +51,34 @@ class _InstalledAppItemState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Text( - widget.name, - maxLines: 2, - overflow: TextOverflow.visible, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(width: 4), - I18nText( - widget.installedVersion.isEmpty - ? 'All versions' - : 'v${widget.installedVersion}', - ), - ], + Text( + widget.name, + maxLines: 2, + overflow: TextOverflow.visible, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), ), Text(widget.pkgName), + I18nText( + FlutterI18n.translate( + context, + 'installed', + translationParams: { + 'version':'v${widget.installedVersion}' + }, + ), + ), Row( children: [ I18nText( - FlutterI18n.translate( - context, - 'suggested', - translationParams: { - 'version': widget.suggestedVersion.isEmpty - ? 'All versions' - : 'v${widget.suggestedVersion}' - }, - ), + 'suggested', + translationParams: { + 'version' : widget.suggestedVersion.isEmpty + ? FlutterI18n.translate(context, 'appSelectorCard.allVersions') + : 'v${widget.suggestedVersion}', + }, ), const SizedBox(width: 4), Text( diff --git a/lib/ui/widgets/appSelectorView/not_installed_app_item.dart b/lib/ui/widgets/appSelectorView/not_installed_app_item.dart index 8c1ae1fc..fa94eb2b 100644 --- a/lib/ui/widgets/appSelectorView/not_installed_app_item.dart +++ b/lib/ui/widgets/appSelectorView/not_installed_app_item.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; class NotInstalledAppItem extends StatefulWidget { @@ -54,12 +55,25 @@ class _NotInstalledAppItem extends State { ), ), const SizedBox(height: 4), - const Text('App not installed.'), - const SizedBox(height: 4), + I18nText( + 'appSelectorCard.notInstalled', + child: Text( + '', + style: TextStyle( + color: + Theme.of(context).textTheme.titleLarge!.color, + ), + ), + ), Row( children: [ - Text( - 'Suggested: ${widget.suggestedVersion.isEmpty ? 'All versions' : 'v${widget.suggestedVersion}'}', + I18nText( + 'suggested', + translationParams: { + 'version' : widget.suggestedVersion.isEmpty + ? FlutterI18n.translate(context, 'appSelectorCard.allVersions') + : 'v${widget.suggestedVersion}', + }, ), const SizedBox(width: 4), Text( diff --git a/lib/ui/widgets/patchesSelectorView/patch_item.dart b/lib/ui/widgets/patchesSelectorView/patch_item.dart index c3cfba80..267fb1c3 100644 --- a/lib/ui/widgets/patchesSelectorView/patch_item.dart +++ b/lib/ui/widgets/patchesSelectorView/patch_item.dart @@ -3,6 +3,7 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/toast.dart'; +import 'package:revanced_manager/ui/widgets/settingsView/settings_experimental_patches.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; @@ -41,137 +42,144 @@ class PatchItem extends StatefulWidget { class _PatchItemState extends State { @override Widget build(BuildContext context) { + widget.isSelected = widget.isSelected && (!widget.isUnsupported || widget._managerAPI.areExperimentalPatchesEnabled()); return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), - child: CustomCard( - backgroundColor: widget.isUnsupported && + child: Opacity( + opacity: widget.isUnsupported && widget._managerAPI.areExperimentalPatchesEnabled() == false - ? Theme.of(context).colorScheme.brightness == Brightness.light - ? Colors.grey[400] - : Colors.grey[700] - : null, - onTap: () { - setState(() { - if (widget.isUnsupported && - !widget._managerAPI.areExperimentalPatchesEnabled()) { - widget.isSelected = false; - widget.toast.showBottom('patchItem.unsupportedPatchVersion'); - } else { - widget.isSelected = !widget.isSelected; - } - }); - widget.onChanged(widget.isSelected); - }, - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: Text( - widget.simpleName, - maxLines: 2, - overflow: TextOverflow.visible, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, + ? 0.5 + : 1, + child: CustomCard( + onTap: () { + setState(() { + if (widget.isUnsupported && + !widget._managerAPI.areExperimentalPatchesEnabled()) { + widget.isSelected = false; + widget.toast.showBottom('patchItem.unsupportedPatchVersion'); + } else { + widget.isSelected = !widget.isSelected; + } + }); + widget.onChanged(widget.isSelected); + }, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: Text( + widget.simpleName, + maxLines: 2, + overflow: TextOverflow.visible, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + const SizedBox(height: 1), + Text( + widget.version, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).colorScheme.secondary, + ), + ), + const SizedBox(height: 4), + Text( + widget.description, + softWrap: true, + overflow: TextOverflow.visible, + style: TextStyle( + fontSize: 14, + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, + ), + ), + ], + ), + ), + Transform.scale( + scale: 1.2, + child: Checkbox( + value: widget.isSelected, + activeColor: Theme.of(context).colorScheme.primary, + checkColor: + Theme.of(context).colorScheme.secondaryContainer, + side: BorderSide( + width: 2.0, + color: Theme.of(context).colorScheme.primary, + ), + onChanged: (newValue) { + setState(() { + if (widget.isUnsupported && + !widget._managerAPI + .areExperimentalPatchesEnabled()) { + widget.isSelected = false; + widget.toast + .showBottom('patchItem.unsupportedPatchVersion'); + } else { + widget.isSelected = newValue!; + } + if (widget.isUnsupported && + widget.isSelected && + !selectedUnsupportedPatches.contains(widget.name)) { + selectedUnsupportedPatches.add(widget.name); + } + }); + widget.onChanged(widget.isSelected); + }, + ), + ) + ], + ), + if (widget.isUnsupported && + widget._managerAPI.areExperimentalPatchesEnabled()) + Row( + children: [ + Padding( + padding: const EdgeInsets.only(top: 8), + child: TextButton.icon( + label: I18nText('warning'), + icon: const Icon(Icons.warning, size: 20.0), + onPressed: () => _showUnsupportedWarningDialog(), + style: ButtonStyle( + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Theme.of(context).colorScheme.secondary, ), ), ), - ], - ), - const SizedBox(height: 1), - Text( - widget.version, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).colorScheme.secondary, - ), - ), - const SizedBox(height: 4), - Text( - widget.description, - softWrap: true, - overflow: TextOverflow.visible, - style: TextStyle( - fontSize: 14, - color: Theme.of(context) - .colorScheme - .onSecondaryContainer, - ), - ), - ], - ), - ), - Transform.scale( - scale: 1.2, - child: Checkbox( - value: widget.isSelected, - activeColor: Theme.of(context).colorScheme.primary, - checkColor: - Theme.of(context).colorScheme.secondaryContainer, - side: BorderSide( - width: 2.0, - color: Theme.of(context).colorScheme.primary, - ), - onChanged: (newValue) { - setState(() { - if (widget.isUnsupported && - !widget._managerAPI - .areExperimentalPatchesEnabled()) { - widget.isSelected = false; - widget.toast - .showBottom('patchItem.unsupportedPatchVersion'); - } else { - widget.isSelected = newValue!; - } - }); - widget.onChanged(widget.isSelected); - }, - ), - ) - ], - ), - if (widget.isUnsupported) - Row( - children: [ - Padding( - padding: const EdgeInsets.only(top: 8), - child: TextButton.icon( - label: I18nText('warning'), - icon: const Icon(Icons.warning, size: 20.0), - onPressed: () => _showUnsupportedWarningDialog(), - style: ButtonStyle( - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide( - color: Theme.of(context).colorScheme.secondary, - ), + backgroundColor: MaterialStateProperty.all( + Colors.transparent, + ), + foregroundColor: MaterialStateProperty.all( + Theme.of(context).colorScheme.secondary, ), ), - backgroundColor: MaterialStateProperty.all( - Colors.transparent, - ), - foregroundColor: MaterialStateProperty.all( - Theme.of(context).colorScheme.secondary, - ), ), ), - ), - ], - ) - else - Container(), - widget.child ?? const SizedBox(), - ], + ], + ) + else + Container(), + widget.child ?? const SizedBox(), + ], + ), ), ), ); diff --git a/lib/ui/widgets/settingsView/settings_experimental_patches.dart b/lib/ui/widgets/settingsView/settings_experimental_patches.dart index b5bf97db..79f5e7fe 100644 --- a/lib/ui/widgets/settingsView/settings_experimental_patches.dart +++ b/lib/ui/widgets/settingsView/settings_experimental_patches.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/widgets/I18nText.dart'; +import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; class SExperimentalPatches extends StatefulWidget { @@ -10,6 +11,7 @@ class SExperimentalPatches extends StatefulWidget { } final _settingsViewModel = SettingsViewModel(); +final List selectedUnsupportedPatches = []; class _SExperimentalPatchesState extends State { @override @@ -32,6 +34,12 @@ class _SExperimentalPatchesState extends State { setState(() { _settingsViewModel.useExperimentalPatches(value); }); + if(!value) { + for (final patch in selectedUnsupportedPatches) { + PatchesSelectorViewModel().selectedPatches.removeWhere((element) => patch == element.name); + } + selectedUnsupportedPatches.clear(); + } }, ); }