chore: merge dev branch to main branch (#945)

This commit is contained in:
Ushie 2023-06-24 19:16:22 +03:00 committed by GitHub
commit 9663e3f0f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 536 additions and 450 deletions

View File

@ -24,7 +24,7 @@ If you wish to translate ReVanced Manager, we're accepting translations on [Crow
## 🛠️ Building Manager from source ## 🛠️ Building Manager from source
1. Setup flutter environment for your [platform](https://docs.flutter.dev/get-started/install) 1. Setup flutter environment for your [platform](https://docs.flutter.dev/get-started/install)
2. Clone the repository locally 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 4. Open the project in terminal
5. Run `flutter pub get` 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) 6. Then `flutter packages pub run build_runner build --delete-conflicting-outputs` (Must be done on each git pull)

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "لم يتم اختيار أي تطبيق", "widgetSubtitle": "لم يتم اختيار أي تطبيق",
"noAppsLabel": "لم يتم العثور على تطبيقات", "noAppsLabel": "لم يتم العثور على تطبيقات",
"currentVersion": "الحالي", "currentVersion": "الحالي",
"recommendedVersion": "موصى به", "recommendedVersion": "موصى به"
"anyVersion": "أي"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "اختر التعديلات", "widgetTitle": "اختر التعديلات",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Heç bir tətbiq seçilməyib", "widgetSubtitle": "Heç bir tətbiq seçilməyib",
"noAppsLabel": "Heç bir tətbiq tapılmadı", "noAppsLabel": "Heç bir tətbiq tapılmadı",
"currentVersion": "Hazırkı", "currentVersion": "Hazırkı",
"recommendedVersion": "Tövsiyyə edilən", "recommendedVersion": "Tövsiyyə edilən"
"anyVersion": "istənilən"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Yamaqları seçin", "widgetTitle": "Yamaqları seçin",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Няма избрано приложение", "widgetSubtitle": "Няма избрано приложение",
"noAppsLabel": "Няма намерени приложения", "noAppsLabel": "Няма намерени приложения",
"currentVersion": "Текуща", "currentVersion": "Текуща",
"recommendedVersion": "Препоръчана", "recommendedVersion": "Препоръчана"
"anyVersion": "всяка"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Изберете модификации", "widgetTitle": "Изберете модификации",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "কোনো অ্যাপ্লিকেশন নির্বাচন করা হয়নি", "widgetSubtitle": "কোনো অ্যাপ্লিকেশন নির্বাচন করা হয়নি",
"noAppsLabel": "কোন অ্যাপ্লিকেশন পাওয়া যায়নি", "noAppsLabel": "কোন অ্যাপ্লিকেশন পাওয়া যায়নি",
"currentVersion": "বর্তমান", "currentVersion": "বর্তমান",
"recommendedVersion": "প্রস্তাবিত", "recommendedVersion": "প্রস্তাবিত"
"anyVersion": "যেকোনো সংস্করণ"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "প্যাচ নির্বাচন করুন", "widgetTitle": "প্যাচ নির্বাচন করুন",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Nebyla vybrána žádná aplikace", "widgetSubtitle": "Nebyla vybrána žádná aplikace",
"noAppsLabel": "Nebyly nalezeny žádné aplikace", "noAppsLabel": "Nebyly nalezeny žádné aplikace",
"currentVersion": "Aktuální", "currentVersion": "Aktuální",
"recommendedVersion": "Doporučeno", "recommendedVersion": "Doporučeno"
"anyVersion": "jakákoliv"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Vybrat záplaty", "widgetTitle": "Vybrat záplaty",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Ingen applikation valgt", "widgetSubtitle": "Ingen applikation valgt",
"noAppsLabel": "Ingen applikationer fundet", "noAppsLabel": "Ingen applikationer fundet",
"currentVersion": "Nuværende", "currentVersion": "Nuværende",
"recommendedVersion": "Anbefalet", "recommendedVersion": "Anbefalet"
"anyVersion": "enhver"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Vælg patches", "widgetTitle": "Vælg patches",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Keine App ausgewählt", "widgetSubtitle": "Keine App ausgewählt",
"noAppsLabel": "Keine Apps gefunden", "noAppsLabel": "Keine Apps gefunden",
"currentVersion": "Aktuell", "currentVersion": "Aktuell",
"recommendedVersion": "Empfohlen", "recommendedVersion": "Empfohlen"
"anyVersion": "beliebig"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Patches auswählen", "widgetTitle": "Patches auswählen",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Δεν έχει επιλεχθεί εφαρμογή", "widgetSubtitle": "Δεν έχει επιλεχθεί εφαρμογή",
"noAppsLabel": "Δεν βρέθηκαν εφαρμογές", "noAppsLabel": "Δεν βρέθηκαν εφαρμογές",
"currentVersion": "Τρέχουσα", "currentVersion": "Τρέχουσα",
"recommendedVersion": "Προτεινόμενη", "recommendedVersion": "Προτεινόμενη"
"anyVersion": "οποιαδήποτε"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Επιλέξτε τροποποιήσεις", "widgetTitle": "Επιλέξτε τροποποιήσεις",

View File

@ -24,11 +24,13 @@
"WIP": "Work in progress...", "WIP": "Work in progress...",
"noInstallations": "No patched applications installed", "noInstallations": "No patched applications installed",
"installed": "Installed", "installed": "Installed",
"installUpdate": "Continue to install the update?",
"updateDialogTitle": "Update Manager", "updateDialogTitle": "Update Manager",
"updateChangelogTitle": "Changelog", "updateChangelogTitle": "Changelog",
"notificationTitle": "Update downloaded", "notificationTitle": "Update downloaded",
"notificationText": "Tap to install the update", "notificationText": "Tap to install the update",
"downloadingMessage": "Downloading update...", "downloadingMessage": "Downloading update...",
"downloadedMessage": "Update downloaded!",
"installingMessage": "Installing update...", "installingMessage": "Installing update...",
"errorDownloadMessage": "Unable to download update", "errorDownloadMessage": "Unable to download update",
"errorInstallMessage": "Unable to install update", "errorInstallMessage": "Unable to install update",
@ -58,9 +60,10 @@
"widgetTitleSelected": "Selected application", "widgetTitleSelected": "Selected application",
"widgetSubtitle": "No application selected", "widgetSubtitle": "No application selected",
"noAppsLabel": "No applications found", "noAppsLabel": "No applications found",
"notInstalled":"App not installed",
"currentVersion": "Current", "currentVersion": "Current",
"suggestedVersion": "Suggested", "suggestedVersion": "Suggested",
"anyVersion": "any" "allVersions": "All versions"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Select patches", "widgetTitle": "Select patches",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Ninguna aplicación seleccionada", "widgetSubtitle": "Ninguna aplicación seleccionada",
"noAppsLabel": "No se encontraron aplicaciones", "noAppsLabel": "No se encontraron aplicaciones",
"currentVersion": "Versión actual", "currentVersion": "Versión actual",
"recommendedVersion": "Versión recomendada", "recommendedVersion": "Versión recomendada"
"anyVersion": "cualquiera"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Seleccionar parches", "widgetTitle": "Seleccionar parches",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Ninguna aplicación seleccionada", "widgetSubtitle": "Ninguna aplicación seleccionada",
"noAppsLabel": "No se encontraron aplicaciones", "noAppsLabel": "No se encontraron aplicaciones",
"currentVersion": "Versión actual", "currentVersion": "Versión actual",
"recommendedVersion": "Versión recomendada", "recommendedVersion": "Versión recomendada"
"anyVersion": "Cualquier versión"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Seleccionar parches", "widgetTitle": "Seleccionar parches",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Sovellusta ei ole valittu", "widgetSubtitle": "Sovellusta ei ole valittu",
"noAppsLabel": "Sovelluksia ei löytynyt", "noAppsLabel": "Sovelluksia ei löytynyt",
"currentVersion": "Nykyinen", "currentVersion": "Nykyinen",
"recommendedVersion": "Suositeltu", "recommendedVersion": "Suositeltu"
"anyVersion": "mikä tahansa"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Valitse paikkaukset", "widgetTitle": "Valitse paikkaukset",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Aucune application sélectionnée", "widgetSubtitle": "Aucune application sélectionnée",
"noAppsLabel": "Aucune application trouvée", "noAppsLabel": "Aucune application trouvée",
"currentVersion": "Version actuelle", "currentVersion": "Version actuelle",
"recommendedVersion": "Version recommandée", "recommendedVersion": "Version recommandée"
"anyVersion": "n'importe laquelle"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Sélectionner les patchs", "widgetTitle": "Sélectionner les patchs",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "कोई ऐप्लकैशन चुना हुआ नहीं", "widgetSubtitle": "कोई ऐप्लकैशन चुना हुआ नहीं",
"noAppsLabel": "कोई एप्लिकेशन नहीं मिला।", "noAppsLabel": "कोई एप्लिकेशन नहीं मिला।",
"currentVersion": "वर्तमान", "currentVersion": "वर्तमान",
"recommendedVersion": "अनुशंसित", "recommendedVersion": "अनुशंसित"
"anyVersion": "कोई"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "पैच चुने", "widgetTitle": "पैच चुने",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Nincs kiválasztott alkalmazás", "widgetSubtitle": "Nincs kiválasztott alkalmazás",
"noAppsLabel": "Nem találhatóak alkalmazások", "noAppsLabel": "Nem találhatóak alkalmazások",
"currentVersion": "Jelenlegi", "currentVersion": "Jelenlegi",
"recommendedVersion": "Ajánlott", "recommendedVersion": "Ajánlott"
"anyVersion": "bármely"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Patchek kiválasztása", "widgetTitle": "Patchek kiválasztása",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Tidak ada aplikasi yang dipilih", "widgetSubtitle": "Tidak ada aplikasi yang dipilih",
"noAppsLabel": "Aplikasi tidak ditemukan", "noAppsLabel": "Aplikasi tidak ditemukan",
"currentVersion": "Saat ini", "currentVersion": "Saat ini",
"recommendedVersion": "Rekomendasi", "recommendedVersion": "Rekomendasi"
"anyVersion": "semua"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Pilih Modifikasi", "widgetTitle": "Pilih Modifikasi",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Nessuna applicazione selezionata", "widgetSubtitle": "Nessuna applicazione selezionata",
"noAppsLabel": "Nessuna applicazione trovata", "noAppsLabel": "Nessuna applicazione trovata",
"currentVersion": "Attuale", "currentVersion": "Attuale",
"recommendedVersion": "Consigliata", "recommendedVersion": "Consigliata"
"anyVersion": "qualsiasi"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Seleziona patch", "widgetTitle": "Seleziona patch",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "선택된 앱 없음", "widgetSubtitle": "선택된 앱 없음",
"noAppsLabel": "앱이 발견되지 않음", "noAppsLabel": "앱이 발견되지 않음",
"currentVersion": "현재 버전", "currentVersion": "현재 버전",
"recommendedVersion": "권장 버전", "recommendedVersion": "권장 버전"
"anyVersion": "모든 버전"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "패치를 선택하세요", "widgetTitle": "패치를 선택하세요",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Nepasirinkta programa", "widgetSubtitle": "Nepasirinkta programa",
"noAppsLabel": "Nerastos jokios programos", "noAppsLabel": "Nerastos jokios programos",
"currentVersion": "Dabartinė", "currentVersion": "Dabartinė",
"recommendedVersion": "Rekomenduojama", "recommendedVersion": "Rekomenduojama"
"anyVersion": "bet kokia"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Pasirinkite modifikacijas", "widgetTitle": "Pasirinkite modifikacijas",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Tiada aplikasi dipilih", "widgetSubtitle": "Tiada aplikasi dipilih",
"noAppsLabel": "Tiada aplikasi ditemui", "noAppsLabel": "Tiada aplikasi ditemui",
"currentVersion": "Semasa", "currentVersion": "Semasa",
"recommendedVersion": "Disyorkan", "recommendedVersion": "Disyorkan"
"anyVersion": "mana-mana"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Pilih modifikasi", "widgetTitle": "Pilih modifikasi",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Nie wybrano aplikacji", "widgetSubtitle": "Nie wybrano aplikacji",
"noAppsLabel": "Nie znaleziono aplikacji", "noAppsLabel": "Nie znaleziono aplikacji",
"currentVersion": "Aktualna", "currentVersion": "Aktualna",
"recommendedVersion": "Zalecana", "recommendedVersion": "Zalecana"
"anyVersion": "dowolna"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Wybierz łatki", "widgetTitle": "Wybierz łatki",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Nenhum aplicativo selecionado", "widgetSubtitle": "Nenhum aplicativo selecionado",
"noAppsLabel": "Nenhum app foi encontrado", "noAppsLabel": "Nenhum app foi encontrado",
"currentVersion": "Atual", "currentVersion": "Atual",
"recommendedVersion": "Recomendado", "recommendedVersion": "Recomendado"
"anyVersion": "todos"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Selecionar patches", "widgetTitle": "Selecionar patches",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Nenhuma aplicação selecionada", "widgetSubtitle": "Nenhuma aplicação selecionada",
"noAppsLabel": "Não foram encontradas aplicações", "noAppsLabel": "Não foram encontradas aplicações",
"currentVersion": "Atual", "currentVersion": "Atual",
"recommendedVersion": "Recomendada", "recommendedVersion": "Recomendada"
"anyVersion": "qualquer"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Selecionar modificações", "widgetTitle": "Selecionar modificações",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Nicio aplicație selectată", "widgetSubtitle": "Nicio aplicație selectată",
"noAppsLabel": "Nu s-au găsit aplicații", "noAppsLabel": "Nu s-au găsit aplicații",
"currentVersion": "Actual", "currentVersion": "Actual",
"recommendedVersion": "Recomandat", "recommendedVersion": "Recomandat"
"anyVersion": "oricare"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Selectați patch-urile", "widgetTitle": "Selectați patch-urile",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Не выбрано приложение", "widgetSubtitle": "Не выбрано приложение",
"noAppsLabel": "Приложения не найдены", "noAppsLabel": "Приложения не найдены",
"currentVersion": "Текущая", "currentVersion": "Текущая",
"recommendedVersion": "Рекомендуемая", "recommendedVersion": "Рекомендуемая"
"anyVersion": "любая"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Выберите патчи", "widgetTitle": "Выберите патчи",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Nie je vybraná žiadna aplikácia", "widgetSubtitle": "Nie je vybraná žiadna aplikácia",
"noAppsLabel": "Nebola nájdená žiadna aplikácia", "noAppsLabel": "Nebola nájdená žiadna aplikácia",
"currentVersion": "Aktuálne", "currentVersion": "Aktuálne",
"recommendedVersion": "Odporúčané", "recommendedVersion": "Odporúčané"
"anyVersion": "akákoľvek"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Vyberte záplaty", "widgetTitle": "Vyberte záplaty",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Nijedna aplikacija nije izabrana", "widgetSubtitle": "Nijedna aplikacija nije izabrana",
"noAppsLabel": "Nijedna aplikacija nije pronađena", "noAppsLabel": "Nijedna aplikacija nije pronađena",
"currentVersion": "Trenutna", "currentVersion": "Trenutna",
"recommendedVersion": "Preporučena", "recommendedVersion": "Preporučena"
"anyVersion": "bilo koja"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Izaberite pečeve", "widgetTitle": "Izaberite pečeve",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Ниједна апликација није изабрана", "widgetSubtitle": "Ниједна апликација није изабрана",
"noAppsLabel": "Ниједна апликација није пронађена", "noAppsLabel": "Ниједна апликација није пронађена",
"currentVersion": "Тренутна", "currentVersion": "Тренутна",
"recommendedVersion": "Препоручена", "recommendedVersion": "Препоручена"
"anyVersion": "било која"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Изаберите печеве", "widgetTitle": "Изаберите печеве",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "Ingen app vald", "widgetSubtitle": "Ingen app vald",
"noAppsLabel": "Inga applikationer hittades", "noAppsLabel": "Inga applikationer hittades",
"currentVersion": "Nuvarande", "currentVersion": "Nuvarande",
"recommendedVersion": "Rekommenderad", "recommendedVersion": "Rekommenderad"
"anyVersion": "alla"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Välj patchar", "widgetTitle": "Välj patchar",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Hakuna programu iliyochaguliwa", "widgetSubtitle": "Hakuna programu iliyochaguliwa",
"noAppsLabel": "Hakuna programu zilizopatikana", "noAppsLabel": "Hakuna programu zilizopatikana",
"currentVersion": "Sasa", "currentVersion": "Sasa",
"recommendedVersion": "Imependekezwa", "recommendedVersion": "Imependekezwa"
"anyVersion": "yoyote"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Chagua viraka", "widgetTitle": "Chagua viraka",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Hiçbir uygulama seçilmedi", "widgetSubtitle": "Hiçbir uygulama seçilmedi",
"noAppsLabel": "Hiçbir uygulama bulunamadı", "noAppsLabel": "Hiçbir uygulama bulunamadı",
"currentVersion": "Şu anki", "currentVersion": "Şu anki",
"recommendedVersion": "Önerilen", "recommendedVersion": "Önerilen"
"anyVersion": "herhangi"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Yamaları Seçin", "widgetTitle": "Yamaları Seçin",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "کوئی ایپلیکیشن منتخب نہیں", "widgetSubtitle": "کوئی ایپلیکیشن منتخب نہیں",
"noAppsLabel": "کوئی ایپلیکیشن نہیں ملی", "noAppsLabel": "کوئی ایپلیکیشن نہیں ملی",
"currentVersion": "موجودہ", "currentVersion": "موجودہ",
"recommendedVersion": "تجویز کردہ", "recommendedVersion": "تجویز کردہ"
"anyVersion": "کوئی بھی"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "پیچز منتخب کریں", "widgetTitle": "پیچز منتخب کریں",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "Chưa chọn ứng dụng nào", "widgetSubtitle": "Chưa chọn ứng dụng nào",
"noAppsLabel": "Không tìm thấy ứng dụng nào", "noAppsLabel": "Không tìm thấy ứng dụng nào",
"currentVersion": "Hiện tại", "currentVersion": "Hiện tại",
"recommendedVersion": "Đề xuất", "recommendedVersion": "Đề xuất"
"anyVersion": "bất kỳ"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "Chọn bản vá", "widgetTitle": "Chọn bản vá",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "未选择应用程序", "widgetSubtitle": "未选择应用程序",
"noAppsLabel": "未发现应用程序。", "noAppsLabel": "未发现应用程序。",
"currentVersion": "当前", "currentVersion": "当前",
"recommendedVersion": "推荐", "recommendedVersion": "推荐"
"anyVersion": "任意"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "选择补丁", "widgetTitle": "选择补丁",

View File

@ -54,8 +54,7 @@
"widgetSubtitle": "沒有選擇應用程式", "widgetSubtitle": "沒有選擇應用程式",
"noAppsLabel": "找不到應用程式", "noAppsLabel": "找不到應用程式",
"currentVersion": "目前版本", "currentVersion": "目前版本",
"recommendedVersion": "推薦版本", "recommendedVersion": "推薦版本"
"anyVersion": "任何版本"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "選擇補丁", "widgetTitle": "選擇補丁",

View File

@ -55,8 +55,7 @@
"widgetSubtitle": "未選取應用程式", "widgetSubtitle": "未選取應用程式",
"noAppsLabel": "找不到應用程式", "noAppsLabel": "找不到應用程式",
"currentVersion": "目前", "currentVersion": "目前",
"recommendedVersion": "建議", "recommendedVersion": "建議"
"anyVersion": "任何"
}, },
"patchSelectorCard": { "patchSelectorCard": {
"widgetTitle": "選取修補檔", "widgetTitle": "選取修補檔",

View File

@ -11,6 +11,6 @@ In order to use ReVanced Manager, certain requirements must be met.
## ⏭️ What's next ## ⏭️ 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) Continue: [⬇️ Installation](1_installation.md)

View File

@ -4,11 +4,11 @@ In order to use ReVanced on your Android device, ReVanced Manager must be instal
## 🪜 Installation steps ## 🪜 Installation steps
- Download the latest version of ReVanced Manager from [here](https://github.com/revanced/revanced-manager/releases/latest) 1. Download the latest version of ReVanced Manager from [here](https://github.com/revanced/revanced-manager/releases/latest)
- Install ReVanced Manager 2. Install ReVanced Manager
## ⏭️ What's next ## ⏭️ 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) Continue: [🪛 Usage](2_usage.md)

View File

@ -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 1. Navigate to the **Patcher** tab from the bottom navigation bar
2. Tap on the **Select an application** card 2. Tap on the **Select an app** card
3. Choose an application to patch[^1] 3. Choose an app 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. > **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] 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. > **Warning**: If you see a warning you can click on it for more information.
5. Tap on the **Done** button to confirm your selection 5. Tap on the **Done** then **Patch** button
6. Tap on the **Patch** button to start patching > **Warning**: The patching process may take ~5 minutes. Exiting the app may increase the time it takes to patch.
> **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. 6. Tap on the **Install** button
7. After patching is complete, tap on the **Install** button to install the patched application > **Note**: If you are rooted, you can mount the patched app on top of the original app.[^3]
> **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 export the patched app to storage using the options in the top right corner.
> Optionally, you may tap on the three vertical dots in the top right to export the patched application to storage.
[^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] [^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** chip above the list of patches. [^2]: It is suggested to use the default set of patches by tapping on the **Default** button above the list of patches.
[^3]: You can obtain `.apk` files from sites such as [APKMirror](https://www.apkmirror.com/). [^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.
[^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.
## ⏭️ What's next ## ⏭️ What's next

View File

@ -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 1. Tap on the **Dashboard** tab in the bottom navigation bar
2. Select the **Installed** chip 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 4. Choose one of the options from the menu
## ⏭️ What's next ## ⏭️ What's next

View File

@ -14,15 +14,15 @@ ReVanced Manager has settings that can be configured to your liking.
- ### 🧪 Experimental ReVanced Patches support - ### 🧪 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 - ### 🧑‍🔬 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 - ### 🔑 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 - ### 📄 Export, import or reset ReVanced Patches selection

View File

@ -1,11 +1,11 @@
# 🛠️ Usage # 🛠️ 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 ## 📖 Table of contents
1. [🧩 Patching applications](2_1_patching.md) 1. [🧩 Patching apps](2_1_patching.md)
2. [🧰 Managing patched applications](2_2_managing.md) 2. [🧰 Managing patched apps](2_2_managing.md)
3. [🔄 Updating ReVanced Manager](2_3_updating.md) 3. [🔄 Updating ReVanced Manager](2_3_updating.md)
4. [⚙️ Configuring ReVanced Manager](2_4_settings.md) 4. [⚙️ Configuring ReVanced Manager](2_4_settings.md)

View File

@ -2,23 +2,27 @@
In case you encounter any issues while using ReVanced Manager, please refer to this page for possible solutions. 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.
- ### ❗Error code `135`, `139` or `1` when patching the application - 💥 App not installed as package conflicts with an existing package
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. 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 ## ⏭️ What's next

View File

@ -7,9 +7,15 @@ This documentation explains how to use [ReVanced Manager](https://github.com/rev
0. [💼 Prerequisites](0_prerequisites.md) 0. [💼 Prerequisites](0_prerequisites.md)
1. [⬇️ Installation](1_installation.md) 1. [⬇️ Installation](1_installation.md)
2. [🛠️ Usage](2_usage.md) 2. [🛠️ Usage](2_usage.md)
1. [🧩 Patching applications](2_1_patching.md) 1. [🧩 Patching apps](2_1_patching.md)
2. [🧰 Managing patched applications](2_2_managing.md) 2. [🧰 Managing patched apps](2_2_managing.md)
3. [🔄 Updating ReVanced Manager](2_3_updating.md) 3. [🔄 Updating ReVanced Manager](2_3_updating.md)
4. [⚙️ Configuring ReVanced Manager](2_4_settings.md) 4. [⚙️ Configuring ReVanced Manager](2_4_settings.md)
3. [🛟 Troubleshooting](3_troubleshooting.md) 3. [🛟 Troubleshooting](3_troubleshooting.md)
4. [🛠 Building from source](4_building.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)

View File

@ -144,30 +144,23 @@ class PatcherAPI {
); );
} }
Future<String> getOriginalFilePath( Future<String> getOriginalFilePath(String packageName) async {
String packageName,
String originalFilePath,
) async {
try { try {
final bool hasRootPermissions = await _rootAPI.hasRootPermissions(); final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) { if (hasRootPermissions) {
originalFilePath = await _rootAPI.getOriginalFilePath( return await _rootAPI.getOriginalFilePath(packageName);
packageName,
originalFilePath,
);
} }
return originalFilePath;
} on Exception catch (e) { } on Exception catch (e) {
if (kDebugMode) { if (kDebugMode) {
print(e); print(e);
} }
return originalFilePath;
} }
return '';
} }
Future<void> runPatcher( Future<void> runPatcher(
String packageName, String packageName,
String originalFilePath, String apkFilePath,
List<Patch> selectedPatches, List<Patch> selectedPatches,
) async { ) async {
final bool includeSettings = await needsSettingsPatch(selectedPatches); final bool includeSettings = await needsSettingsPatch(selectedPatches);
@ -198,15 +191,16 @@ class PatcherAPI {
_outFile = File('${workDir.path}/out.apk'); _outFile = File('${workDir.path}/out.apk');
final Directory cacheDir = Directory('${workDir.path}/cache'); final Directory cacheDir = Directory('${workDir.path}/cache');
cacheDir.createSync(); cacheDir.createSync();
String originalFilePath = await getOriginalFilePath(packageName);
if (originalFilePath.isEmpty) {
originalFilePath = apkFilePath;
}
try { try {
await patcherChannel.invokeMethod( await patcherChannel.invokeMethod(
'runPatcher', 'runPatcher',
{ {
'patchBundleFilePath': patchBundleFile.path, 'patchBundleFilePath': patchBundleFile.path,
'originalFilePath': await getOriginalFilePath( 'originalFilePath': originalFilePath,
packageName,
originalFilePath,
),
'inputFilePath': inputFile.path, 'inputFilePath': inputFile.path,
'patchedFilePath': patchedFile.path, 'patchedFilePath': patchedFile.path,
'outFilePath': _outFile!.path, 'outFilePath': _outFile!.path,

View File

@ -140,7 +140,7 @@ class RevancedAPI {
return null; return null;
} }
StreamController<double> managerUpdateProgress = StreamController<double>(); StreamController<double> managerUpdateProgress = StreamController<double>.broadcast();
void updateManagerDownloadProgress(int progress) { void updateManagerDownloadProgress(int progress) {
managerUpdateProgress.add(progress.toDouble()); managerUpdateProgress.add(progress.toDouble());
@ -170,6 +170,7 @@ class RevancedAPI {
updateManagerDownloadProgress(progress); updateManagerDownloadProgress(progress);
} else if (result is FileInfo) { } else if (result is FileInfo) {
disposeManagerUpdateProgress();
// The download is complete; convert the FileInfo object to a File object // The download is complete; convert the FileInfo object to a File object
outputFile = File(result.file.path); outputFile = File(result.file.path);
} }

View File

@ -2,7 +2,9 @@ import 'package:flutter/foundation.dart';
import 'package:root/root.dart'; import 'package:root/root.dart';
class RootAPI { 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 _postFsDataDirPath = '/data/adb/post-fs-data.d';
final String _serviceDDirPath = '/data/adb/service.d'; final String _serviceDDirPath = '/data/adb/service.d';
@ -40,55 +42,62 @@ class RootAPI {
seLinux, seLinux,
String filePath, String filePath,
) async { ) 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<bool> 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<List<String>> getInstalledApps() async {
try { try {
final String? res = await Root.exec( if (permissions.isNotEmpty) {
cmd: 'ls "$_managerDirPath"', await Root.exec(
); cmd: 'chmod $permissions "$filePath"',
if (res != null) { );
final List<String> apps = res.split('\n'); }
apps.removeWhere((pack) => pack.isEmpty); if (ownerGroup.isNotEmpty) {
return apps.map((pack) => pack.trim()).toList(); await Root.exec(
cmd: 'chown $ownerGroup "$filePath"',
);
}
if (seLinux.isNotEmpty) {
await Root.exec(
cmd: 'chcon $seLinux "$filePath"',
);
} }
} on Exception catch (e) { } on Exception catch (e) {
if (kDebugMode) { if (kDebugMode) {
print(e); print(e);
} }
return List.empty();
} }
return List.empty(); }
Future<bool> isAppInstalled(String packageName) async {
if (packageName.isNotEmpty) {
return fileExists('$_serviceDDirPath/$packageName.sh');
}
return false;
}
Future<List<String>> getInstalledApps() async {
final List<String> apps = List.empty();
try {
String? res = await Root.exec(
cmd: 'ls "$_revancedDirPath"',
);
if (res != null) {
final List<String> 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<String> 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<void> deleteApp(String packageName, String originalFilePath) async { Future<void> deleteApp(String packageName, String originalFilePath) async {
@ -98,8 +107,12 @@ class RootAPI {
await Root.exec( await Root.exec(
cmd: 'su -mm -c "umount -l $originalFilePath"', cmd: 'su -mm -c "umount -l $originalFilePath"',
); );
// TODO(ponces): remove in the future, keep it for now during migration.
await Root.exec( await Root.exec(
cmd: 'rm -rf "$_managerDirPath/$packageName"', cmd: 'rm -rf "$_revancedOldDirPath/$packageName"',
);
await Root.exec(
cmd: 'rm -rf "$_revancedDirPath/$packageName"',
); );
await Root.exec( await Root.exec(
cmd: 'rm -rf "$_serviceDDirPath/$packageName.sh"', cmd: 'rm -rf "$_serviceDDirPath/$packageName.sh"',
@ -117,13 +130,13 @@ class RootAPI {
try { try {
await deleteApp(packageName, originalFilePath); await deleteApp(packageName, originalFilePath);
await Root.exec( await Root.exec(
cmd: 'mkdir -p "$_managerDirPath/$packageName"', cmd: 'mkdir -p "$_revancedDirPath/$packageName"',
); );
await setPermissions( await setPermissions(
'0755', '0755',
'shell:shell', 'shell:shell',
'', '',
'$_managerDirPath/$packageName', '$_revancedDirPath/$packageName',
); );
await saveOriginalFilePath(packageName, originalFilePath); await saveOriginalFilePath(packageName, originalFilePath);
await installServiceDScript(packageName); await installServiceDScript(packageName);
@ -142,7 +155,7 @@ class RootAPI {
Future<void> installServiceDScript(String packageName) async { Future<void> installServiceDScript(String packageName) async {
final String content = '#!/system/bin/sh\n' final String content = '#!/system/bin/sh\n'
'while [ "\$(getprop sys.boot_completed | tr -d \'"\'"\'\\\\r\'"\'"\')" != "1" ]; do sleep 3; done\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' 'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
r'[ ! -z $stock_path ] && mount -o bind $base_path $stock_path'; r'[ ! -z $stock_path ] && mount -o bind $base_path $stock_path';
final String scriptFilePath = '$_serviceDDirPath/$packageName.sh'; final String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
@ -164,7 +177,7 @@ class RootAPI {
} }
Future<void> installApk(String packageName, String patchedFilePath) async { Future<void> installApk(String packageName, String patchedFilePath) async {
final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk'; final String newPatchedFilePath = '$_revancedDirPath/$packageName/base.apk';
await Root.exec( await Root.exec(
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"', cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
); );
@ -177,7 +190,7 @@ class RootAPI {
} }
Future<void> mountApk(String packageName, String originalFilePath) async { Future<void> mountApk(String packageName, String originalFilePath) async {
final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk'; final String newPatchedFilePath = '$_revancedDirPath/$packageName/base.apk';
await Root.exec( await Root.exec(
cmd: 'am force-stop "$packageName"', cmd: 'am force-stop "$packageName"',
); );
@ -196,21 +209,18 @@ class RootAPI {
return res != null && res.isNotEmpty; return res != null && res.isNotEmpty;
} }
Future<String> getOriginalFilePath( Future<String> getOriginalFilePath(String packageName) async {
String packageName, final String originalPath = '$_revancedDirPath/$packageName/original.apk';
String originalFilePath, final String oldOrigPath = '$_revancedOldDirPath/$packageName/original.apk';
) async {
final bool isInstalled = await isAppInstalled(packageName); final bool isInstalled = await isAppInstalled(packageName);
if (isInstalled && await isMounted(packageName)) { if (isInstalled && await isMounted(packageName)) {
originalFilePath = '$_managerDirPath/$packageName/original.apk'; if (await fileExists(originalPath)) {
await setPermissions( return originalPath;
'0644', } else if (await fileExists(oldOrigPath)) {
'shell:shell', return oldOrigPath;
'u:object_r:apk_data_file:s0', }
originalFilePath,
);
} }
return originalFilePath; return '';
} }
Future<void> saveOriginalFilePath( Future<void> saveOriginalFilePath(
@ -218,15 +228,15 @@ class RootAPI {
String originalFilePath, String originalFilePath,
) async { ) async {
final String originalRootPath = final String originalRootPath =
'$_managerDirPath/$packageName/original.apk'; '$_revancedDirPath/$packageName/original.apk';
await Root.exec( await Root.exec(
cmd: 'mkdir -p "$_managerDirPath/$packageName"', cmd: 'mkdir -p "$_revancedDirPath/$packageName"',
); );
await setPermissions( await setPermissions(
'0755', '0755',
'shell:shell', 'shell:shell',
'', '',
'$_managerDirPath/$packageName', '$_revancedDirPath/$packageName',
); );
await Root.exec( await Root.exec(
cmd: 'cp "$originalFilePath" "$originalRootPath"', cmd: 'cp "$originalFilePath" "$originalRootPath"',
@ -238,4 +248,18 @@ class RootAPI {
originalFilePath, originalFilePath,
); );
} }
Future<bool> 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;
}
}
} }

View File

@ -1,7 +1,6 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:app_installer/app_installer.dart'; import 'package:app_installer/app_installer.dart';
import 'package:cross_connectivity/cross_connectivity.dart'; import 'package:cross_connectivity/cross_connectivity.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -39,6 +38,7 @@ class HomeViewModel extends BaseViewModel {
List<PatchedApplication> patchedInstalledApps = []; List<PatchedApplication> patchedInstalledApps = [];
List<PatchedApplication> patchedUpdatableApps = []; List<PatchedApplication> patchedUpdatableApps = [];
String? _latestManagerVersion = ''; String? _latestManagerVersion = '';
File? downloadedApk;
Future<void> initialize(BuildContext context) async { Future<void> initialize(BuildContext context) async {
_latestManagerVersion = await _managerAPI.getLatestManagerVersion(); _latestManagerVersion = await _managerAPI.getLatestManagerVersion();
@ -162,74 +162,134 @@ class HomeViewModel extends BaseViewModel {
} }
Future<void> updateManager(BuildContext context) async { Future<void> updateManager(BuildContext context) async {
final ValueNotifier<bool> downloaded = ValueNotifier(false);
try { try {
_toast.showBottom('homeView.downloadingMessage'); _toast.showBottom('homeView.downloadingMessage');
showDialog( showDialog(
context: context, context: context,
builder: (context) => SimpleDialog( builder: (context) => ValueListenableBuilder(
contentPadding: const EdgeInsets.all(16.0), valueListenable: downloaded,
title: I18nText( builder: (context, value, child) {
'homeView.downloadingMessage', return SimpleDialog(
child: Text( contentPadding: const EdgeInsets.all(16.0),
'', title: I18nText(
style: TextStyle( !value
fontSize: 20, ? 'homeView.downloadingMessage'
fontWeight: FontWeight.w500, : 'homeView.downloadedMessage',
color: Theme.of(context).colorScheme.secondary, child: Text(
), '',
), style: TextStyle(
), fontSize: 20,
children: [ fontWeight: FontWeight.w500,
Column( color: Theme.of(context).colorScheme.secondary,
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<double>(
initialData: 0.0,
stream: _revancedAPI.managerUpdateProgress.stream,
builder: (context, snapshot) {
return LinearProgressIndicator(
value: snapshot.data! * 0.01,
valueColor: AlwaysStoppedAnimation<Color>(
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();
},
), ),
), ),
),
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<double>(
initialData: 0.0,
stream: _revancedAPI.managerUpdateProgress.stream,
builder: (context, snapshot) {
return LinearProgressIndicator(
value: snapshot.data! * 0.01,
valueColor: AlwaysStoppedAnimation<Color>(
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(); final File? managerApk = await downloadManager();
if (managerApk != null) { if (managerApk != null) {
downloaded.value = true;
downloadedApk = managerApk;
// await flutterLocalNotificationsPlugin.zonedSchedule( // await flutterLocalNotificationsPlugin.zonedSchedule(
// 0, // 0,
// FlutterI18n.translate( // FlutterI18n.translate(

View File

@ -133,7 +133,7 @@ class PatcherViewModel extends BaseViewModel {
if (suggestedVersion.isEmpty) { if (suggestedVersion.isEmpty) {
suggestedVersion = FlutterI18n.translate( suggestedVersion = FlutterI18n.translate(
context, context,
'appSelectorCard.anyVersion', 'appSelectorCard.allVersions',
); );
} else { } else {
suggestedVersion = 'v$suggestedVersion'; suggestedVersion = 'v$suggestedVersion';

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
import 'package:skeletons/skeletons.dart'; import 'package:skeletons/skeletons.dart';
class AppSkeletonLoader extends StatelessWidget { class AppSkeletonLoader extends StatelessWidget {
@ -7,59 +8,64 @@ class AppSkeletonLoader extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width; final screenWidth = MediaQuery.of(context).size.width;
return Skeleton( return ListView.builder(
isLoading: true, shrinkWrap: true,
skeleton: ListView.builder( itemCount: 7,
shrinkWrap: true, padding: EdgeInsets.zero,
itemCount: 7, itemBuilder: (context, index) => Padding(
itemBuilder: (context, index) => Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12.0),
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8.0), child: CustomCard(
child: SkeletonItem( child: Row(
child: Row( children: [
children: <Widget>[ SkeletonAvatar(
SkeletonAvatar( style: SkeletonAvatarStyle(
style: SkeletonAvatarStyle( width: screenWidth * 0.10,
width: screenWidth * 0.15, height: screenWidth * 0.10,
height: screenWidth * 0.15, borderRadius: const BorderRadius.all(Radius.circular(12)),
borderRadius: const BorderRadius.all(Radius.circular(12)), ),
),
const SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
width: screenWidth * 0.4,
child: SkeletonLine(
style: SkeletonLineStyle(
height: 20,
width: screenWidth * 0.4,
borderRadius: const BorderRadius.all(Radius.circular(10)),
),
),
), ),
), const SizedBox(height: 12),
const SizedBox(width: 16), SizedBox(
Column( width: screenWidth * 0.6,
crossAxisAlignment: CrossAxisAlignment.start, child: SkeletonLine(
children: <Widget>[ style: SkeletonLineStyle(
Container( height: 15,
color: Colors.white, width: screenWidth * 0.6,
height: 34, borderRadius: const BorderRadius.all(Radius.circular(10)),
width: screenWidth * 0.4,
child: SkeletonParagraph(
style: const SkeletonParagraphStyle(
lines: 1,
),
), ),
), ),
const SizedBox(height: 12), ),
Container( const SizedBox(height: 5),
margin: const EdgeInsets.only(bottom: 4), SizedBox(
color: Colors.white, width: screenWidth * 0.5,
height: 34, child: SkeletonLine(
width: screenWidth * 0.6, style: SkeletonLineStyle(
child: SkeletonParagraph( height: 15,
style: const SkeletonParagraphStyle( width: screenWidth * 0.5,
lines: 1, borderRadius: const BorderRadius.all(Radius.circular(10)),
),
), ),
), ),
], ),
), ],
], ),
), ],
), ),
), ),
), ),
child: const Center(
child: Text('Content'),
),
); );
} }
} }

View File

@ -1,5 +1,4 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
@ -52,38 +51,34 @@ class _InstalledAppItemState extends State<InstalledAppItem> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Row( Text(
children: [ widget.name,
Text( maxLines: 2,
widget.name, overflow: TextOverflow.visible,
maxLines: 2, style: const TextStyle(
overflow: TextOverflow.visible, fontSize: 16,
style: const TextStyle( fontWeight: FontWeight.w500,
fontSize: 16, ),
fontWeight: FontWeight.w500,
),
),
const SizedBox(width: 4),
I18nText(
widget.installedVersion.isEmpty
? 'All versions'
: 'v${widget.installedVersion}',
),
],
), ),
Text(widget.pkgName), Text(widget.pkgName),
I18nText(
FlutterI18n.translate(
context,
'installed',
translationParams: {
'version':'v${widget.installedVersion}'
},
),
),
Row( Row(
children: [ children: [
I18nText( I18nText(
FlutterI18n.translate( 'suggested',
context, translationParams: {
'suggested', 'version' : widget.suggestedVersion.isEmpty
translationParams: { ? FlutterI18n.translate(context, 'appSelectorCard.allVersions')
'version': widget.suggestedVersion.isEmpty : 'v${widget.suggestedVersion}',
? 'All versions' },
: 'v${widget.suggestedVersion}'
},
),
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
class NotInstalledAppItem extends StatefulWidget { class NotInstalledAppItem extends StatefulWidget {
@ -54,12 +55,25 @@ class _NotInstalledAppItem extends State<NotInstalledAppItem> {
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
const Text('App not installed.'), I18nText(
const SizedBox(height: 4), 'appSelectorCard.notInstalled',
child: Text(
'',
style: TextStyle(
color:
Theme.of(context).textTheme.titleLarge!.color,
),
),
),
Row( Row(
children: [ children: [
Text( I18nText(
'Suggested: ${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), const SizedBox(width: 4),
Text( Text(

View File

@ -3,6 +3,7 @@ import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/toast.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_card.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
@ -41,137 +42,144 @@ class PatchItem extends StatefulWidget {
class _PatchItemState extends State<PatchItem> { class _PatchItemState extends State<PatchItem> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
widget.isSelected = widget.isSelected && (!widget.isUnsupported || widget._managerAPI.areExperimentalPatchesEnabled());
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0), padding: const EdgeInsets.symmetric(vertical: 4.0),
child: CustomCard( child: Opacity(
backgroundColor: widget.isUnsupported && opacity: widget.isUnsupported &&
widget._managerAPI.areExperimentalPatchesEnabled() == false widget._managerAPI.areExperimentalPatchesEnabled() == false
? Theme.of(context).colorScheme.brightness == Brightness.light ? 0.5
? Colors.grey[400] : 1,
: Colors.grey[700] child: CustomCard(
: null, onTap: () {
onTap: () { setState(() {
setState(() { if (widget.isUnsupported &&
if (widget.isUnsupported && !widget._managerAPI.areExperimentalPatchesEnabled()) {
!widget._managerAPI.areExperimentalPatchesEnabled()) { widget.isSelected = false;
widget.isSelected = false; widget.toast.showBottom('patchItem.unsupportedPatchVersion');
widget.toast.showBottom('patchItem.unsupportedPatchVersion'); } else {
} else { widget.isSelected = !widget.isSelected;
widget.isSelected = !widget.isSelected; }
} });
}); widget.onChanged(widget.isSelected);
widget.onChanged(widget.isSelected); },
}, child: Column(
child: Column( children: <Widget>[
children: <Widget>[ Row(
Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
children: <Widget>[ Flexible(
Flexible( child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
children: <Widget>[ Row(
Row( crossAxisAlignment: CrossAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[
children: <Widget>[ Expanded(
Expanded( child: Text(
child: Text( widget.simpleName,
widget.simpleName, maxLines: 2,
maxLines: 2, overflow: TextOverflow.visible,
overflow: TextOverflow.visible, style: const TextStyle(
style: const TextStyle( fontSize: 16,
fontSize: 16, fontWeight: FontWeight.w600,
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: <Widget>[
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,
const SizedBox(height: 1), ),
Text( foregroundColor: MaterialStateProperty.all(
widget.version, Theme.of(context).colorScheme.secondary,
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: <Widget>[
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,
),
), ),
), ),
), ],
], )
) else
else Container(),
Container(), widget.child ?? const SizedBox(),
widget.child ?? const SizedBox(), ],
], ),
), ),
), ),
); );

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.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'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
class SExperimentalPatches extends StatefulWidget { class SExperimentalPatches extends StatefulWidget {
@ -10,6 +11,7 @@ class SExperimentalPatches extends StatefulWidget {
} }
final _settingsViewModel = SettingsViewModel(); final _settingsViewModel = SettingsViewModel();
final List<String> selectedUnsupportedPatches = [];
class _SExperimentalPatchesState extends State<SExperimentalPatches> { class _SExperimentalPatchesState extends State<SExperimentalPatches> {
@override @override
@ -32,6 +34,12 @@ class _SExperimentalPatchesState extends State<SExperimentalPatches> {
setState(() { setState(() {
_settingsViewModel.useExperimentalPatches(value); _settingsViewModel.useExperimentalPatches(value);
}); });
if(!value) {
for (final patch in selectedUnsupportedPatches) {
PatchesSelectorViewModel().selectedPatches.removeWhere((element) => patch == element.name);
}
selectedUnsupportedPatches.clear();
}
}, },
); );
} }