Compare commits

...

20 Commits

Author SHA1 Message Date
semantic-release-bot
3c36950aeb chore(release): 1.21.0-dev.5 [skip ci]
# [1.21.0-dev.5](https://github.com/ReVanced/revanced-manager/compare/v1.21.0-dev.4...v1.21.0-dev.5) (2024-06-26)

### Bug Fixes

* Adjust scroll from clipping children form fields in `AlertDialog` from `showSourcesDialog` ([#1782](https://github.com/ReVanced/revanced-manager/issues/1782)) ([bbeb836](bbeb836923))
2024-06-26 22:53:56 +00:00
DMzS
bbeb836923 fix: Adjust scroll from clipping children form fields in AlertDialog from showSourcesDialog (#1782) 2024-06-27 05:45:55 +07:00
semantic-release-bot
a99406f0a9 chore(release): 1.21.0-dev.4 [skip ci]
# [1.21.0-dev.4](https://github.com/ReVanced/revanced-manager/compare/v1.21.0-dev.3...v1.21.0-dev.4) (2024-06-24)

### Bug Fixes

* Cache external API calls  ([#1911](https://github.com/ReVanced/revanced-manager/issues/1911)) ([2c3e2e6](2c3e2e639f))
* Follow language update immediately ([#1944](https://github.com/ReVanced/revanced-manager/issues/1944)) ([c13827e](c13827e8e1))
* SecurityException when patching application ([#1856](https://github.com/ReVanced/revanced-manager/issues/1856)) ([e0a6de2](e0a6de2c2b))
* Update dialog shows dev version & loading gets stuck in certain circumstances ([#1792](https://github.com/ReVanced/revanced-manager/issues/1792)) ([fc52560](fc52560244))

### Features

* Add ability to set `null` in patch options ([#1947](https://github.com/ReVanced/revanced-manager/issues/1947)) ([5c68d51](5c68d513a3))
2024-06-24 17:03:46 +00:00
Pun Butrach
73368b58be build: Support for Flutter 3.22 (#1921)
Signed-off-by: validcube <pun.butrach@gmail.com>
2024-06-24 23:55:37 +07:00
ReVanced Bot
ca14e77ba3 chore: Sync translations (#1899) 2024-06-24 23:55:03 +07:00
Pun Butrach
cafdfcda47 ci: Don't fail validation on unimportant warnings 2024-06-24 23:52:23 +07:00
aAbed
5c68d513a3 feat: Add ability to set null in patch options (#1947) 2024-06-24 23:37:16 +07:00
kitadai31
fc52560244 fix: Update dialog shows dev version & loading gets stuck in certain circumstances (#1792)
Signed-off-by: validcube <pun.butrach@gmail.com>
Co-authored-by: validcube <pun.butrach@gmail.com>
2024-06-19 14:44:09 +07:00
Pun Butrach
46f6a49a7a ci: Always run on dev branch only 2024-06-15 17:36:25 +07:00
Snehith
c13827e8e1 fix: Follow language update immediately (#1944)
Co-authored-by: surya-technovert <surya.m@technovert.com>
2024-06-15 17:21:47 +07:00
Pun Butrach
e0a6de2c2b fix: SecurityException when patching application (#1856) 2024-05-28 11:36:31 +07:00
yonggamer
afdba00722 build: Fix invalid Gradle wrapper checksum (#1919) 2024-05-28 09:31:48 +07:00
oSumAtrIX
9084c71aa3 build: Bump dependencies 2024-05-26 01:21:14 +02:00
oSumAtrIX
8fc5fb6a80 docs: Improve issue templates 2024-05-26 00:43:38 +02:00
validcube
5f762c5442 build: Update Dart dependencies
Signed-off-by: validcube <pun.butrach@gmail.com>
2024-05-19 19:15:35 +07:00
Pun Butrach
8b21ec1ea3 ci: Switch to Flutter instead 2024-05-19 18:54:48 +07:00
Pun Butrach
e83fbb864e ci: Run slang first before validating translation 2024-05-19 18:46:49 +07:00
KobeW50
f03af17f71 docs: Fix issue template mistakes (#1910) 2024-05-19 01:33:18 +07:00
kitadai31
2c3e2e639f fix: Cache external API calls (#1911) 2024-05-18 10:52:13 -07:00
KobeW50
cc85b393dc docs: Fix punctuation in issue forms (#1909) 2024-05-18 01:18:29 +07:00
44 changed files with 931 additions and 990 deletions

View File

@@ -70,7 +70,7 @@ body:
Before creating a new bug report, please keep the following in mind:
- **Do not submit a duplicate bug report**: You can review existing bug reports [here](https://github.com/ReVanced/revanced-manager/labels/Bug%20report).
- **Do not submit a duplicate bug report**: Search for existing bug reports [here](https://github.com/ReVanced/revanced-manager/issues?q=label%3A%22Bug+report%22).
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-manager/blob/main/CONTRIBUTING.md).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea
@@ -99,14 +99,14 @@ body:
- type: textarea
attributes:
label: ReVanced Manager logs
description: Export logs in ReVanced Manager settings.
description: Export logs from the ReVanced Manager settings.
render: shell
validations:
required: true
- type: textarea
attributes:
label: Patch logs
description: Export logs in "Patcher" screen.
description: Export logs from the "Patcher" screen.
render: shell
validations:
required: false
@@ -116,11 +116,11 @@ body:
label: Acknowledgements
description: Your bug report will be closed if you don't follow the checklist below.
options:
- label: This issue is not a duplicate of an existing bug report.
- label: I have checked all open and closed bug reports and this is not a duplicate.
required: true
- label: I have chosen an appropriate title.
required: true
- label: All requested information has been provided properly.
required: true
- label: The bug is only related to ReVanced Manager
- label: The bug is only related to ReVanced Manager.
required: true

View File

@@ -70,7 +70,7 @@ body:
Before creating a new feature request, please keep the following in mind:
- **Do not submit a duplicate feature request**: You can review existing feature requests [here](https://github.com/ReVanced/revanced-manager//labels/Feature%20request).
- **Do not submit a duplicate feature request**: Search for existing feature requests [here](https://github.com/ReVanced/revanced-manager/issues?q=label%3A%22Feature+request%22).
- **Review the contribution guidelines**: Make sure your feature request adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-manager/blob/main/CONTRIBUTING.md).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea
@@ -79,7 +79,6 @@ body:
description: |
- Describe your feature in detail
- Add images, videos, links, examples, references, etc. if possible
- Add the target application name in case you request a new patch
- type: textarea
attributes:
label: Motivation
@@ -98,9 +97,9 @@ body:
label: Acknowledgements
description: Your feature request will be closed if you don't follow the checklist below.
options:
- label: This issue is not a duplicate of an existing feature request.
- label: I have checked all open and closed feature requests and this is not a duplicate.
required: true
- label: I have chosen an appropriate title.
required: true
- label: The feature request is only related to ReVanced Manager
- label: The feature request is only related to ReVanced Manager.
required: true

View File

@@ -61,6 +61,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: 3.22.x
cache: ${{ inputs.flutter-cache }}
- name: Get dependencies

View File

@@ -36,6 +36,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: 3.22.x
- name: Install dependencies
run: npm install

View File

@@ -5,6 +5,7 @@ on:
schedule:
- cron: 00 12 * * 1
push:
branches: dev
paths:
- assets/i18n/*.json
- assets/i18n/*.dart
@@ -20,8 +21,11 @@ jobs:
with:
fetch-depth: 0
- name: Setup Dart
uses: dart-lang/setup-dart@v1
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
cache: true
flutter-version: 3.22.x
- name: Sync translations from Crowdin
uses: crowdin/github-action@v1
@@ -51,7 +55,9 @@ jobs:
- name: Validation of Translation Strings
run: |
dart analyze lib/gen/strings.g.dart
dart pub get
dart run slang
flutter analyze lib/gen/strings.g.dart --no-fatal-infos --no-fatal-warnings
- name: Commit translations
run: |

View File

@@ -23,10 +23,11 @@ if (flutterVersionName == null) {
}
android {
compileSdk flutter.compileSdkVersion
compileSdk 34
ndkVersion flutter.ndkVersion
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
@@ -113,6 +114,7 @@ flutter {
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") // https://pub.dev/packages/flutter_local_notifications#gradle-setup
implementation("app.revanced:revanced-patcher:19.3.1")
implementation("app.revanced:revanced-library:2.2.1")
}

View File

@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -18,8 +18,8 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.1.2" apply false
id "org.jetbrains.kotlin.android" version "1.9.23" apply false
id "com.android.application" version "8.4.1" apply false
id "org.jetbrains.kotlin.android" version "2.0.0" apply false
}
include ":app"

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Custom value",
"setToNull": "Set to null",
"nullValue": "This option value is currently null",
"resetOptionsTooltip": "Reset patch options",
"viewTitle": "Patch options",
"saveOptions": "Save",
"addOptions": "Add options",
"deselectPatch": "Deselect patch",
"unselectPatch": "Unselect patch",
"tooltip": "More input options",
"selectFilePath": "Select file path",
"selectFolder": "Select folder",
"selectOption": "Select option",
"requiredOption": "This option is required",
"requiredOption": "Setting this option is required",
"unsupportedOption": "This option is not supported",
"requiredOptionNull": "The following options have to be set:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "تخصيص القيمة",
"setToNull": "تعيين إلى فارغ",
"nullValue": "قيمة هذا الخيار فارغة حاليا",
"resetOptionsTooltip": "إعادة تعيين خيارات التعديل",
"viewTitle": "خيارات التعديل",
"saveOptions": "حفظ",
"addOptions": "إضافة خيارات",
"deselectPatch": "إلغاء تحديد التعديل",
"unselectPatch": "إلغاء تحديد التصحيح",
"tooltip": "المزيد من خيارات الإدخال",
"selectFilePath": "تحديد مسار الملف",
"selectFolder": "تحديد مجلد",
"selectOption": "تحديد خيار",
"requiredOption": "هذا الخيار مطلوب",
"requiredOption": "إعداد هذا الخيار مطلوب",
"unsupportedOption": "هذا الخيار غير مدعوم",
"requiredOptionNull": "يجب تعيين الخيارات التالية:\n\n${options}"
},
@@ -143,7 +145,7 @@
"installType": "تحديد نوع التثبيت",
"installTypeDescription": "تحديد نوع التثبيت للمتابعة.",
"installButton": "تثبيت",
"installRootType": "تحميل",
"installRootType": "Mount",
"installNonRootType": "عادي",
"warning": "قم بتعطيل التحديثات التلقائية للتطبيق المعدل لتجنب المشكلات غير المتوقعة.",
"pressBackAgain": "اضغط رجوع مرة اخرى للإلغاء",
@@ -255,14 +257,14 @@
"widgetTitle": "معلومات التطبيق",
"openButton": "فتح",
"uninstallButton": "إلغاء التثبيت",
"unmountButton": "إلغاء التحميل",
"unmountButton": "Unmount",
"rootDialogTitle": "خطأ",
"unmountDialogText": "هل أنت متأكد أنك تريد إلغاء تحميل هذا التطبيق؟",
"unmountDialogText": "هل أنت متأكد من أنك تريد Unmount لهذا التطبيق؟",
"uninstallDialogText": "هل أنت متأكد من أنك تريد إلغاء تثبيت هذا التطبيق؟",
"rootDialogText": "تم تثبيت التطبيق بأذونات المستخدم المتميز، لكن ReVanced Manager ليس لديه أذونات حاليًا.\nالرجاء منح أذونات المستخدم المتميز أولاً.",
"packageNameLabel": "اسم الحُزْمَة",
"installTypeLabel": "نوع التثبيت",
"mountTypeLabel": "تحميل",
"mountTypeLabel": "Mount",
"regularTypeLabel": "عادي",
"patchedDateLabel": "تاريخ التعديل",
"appliedPatchesLabel": "التعديلات المطبقة",

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Vlastní hodnota",
"setToNull": "Nastavit na null",
"nullValue": "Tato hodnota volby je v současné době null",
"resetOptionsTooltip": "Obnovit nastavení záplat",
"viewTitle": "Nastavení záplat",
"saveOptions": "Uložit",
"addOptions": "Přidat možnosti",
"deselectPatch": "Odznačit záplatu",
"unselectPatch": "Zrušit výběr patch",
"tooltip": "Další možnosti vstupu",
"selectFilePath": "Zvolte cestu k souboru",
"selectFolder": "Vybrat složku",
"selectOption": "Vybrat možnost",
"requiredOption": "Tato možnost je vyžadována",
"requiredOption": "Nastavení této možnosti je vyžadováno",
"unsupportedOption": "Tato možnost není podporována",
"requiredOptionNull": "Tyto možnosti musí být nastaveny:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Tilpasset værdi",
"setToNull": "Sæt til nul",
"nullValue": "Denne valgmulighed værdi er i øjeblikket nul",
"resetOptionsTooltip": "Nulstil patch indstillinger",
"viewTitle": "Patch indstillinger",
"saveOptions": "Gem",
"addOptions": "Tilføj indstillinger",
"deselectPatch": "Fravælg patch",
"unselectPatch": "Fravælg patch",
"tooltip": "Flere input-indstillinger",
"selectFilePath": "Vælg fil sti",
"selectFolder": "Vælg mappe",
"selectOption": "Vælg indstilling",
"requiredOption": "Denne indstilling er påkrævet",
"requiredOption": "Indstilling af denne indstilling er påkrævet",
"unsupportedOption": "Denne indstilling understøttes ikke",
"requiredOptionNull": "Følgende indstillinger skal indstilles:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Benutzerdefinierter Wert",
"setToNull": "Auf Null setzen",
"nullValue": "Dieser Optionswert ist derzeit null",
"resetOptionsTooltip": "Patch-Optionen zurücksetzen",
"viewTitle": "Patch-Optionen",
"saveOptions": "Speichern",
"addOptions": "Option hinzufügen",
"deselectPatch": "Patch abwählen",
"unselectPatch": "Patch entfernen",
"tooltip": "Weitere Eingabeoptionen",
"selectFilePath": "Dateipfad auswählen",
"selectFolder": "Ordner auswählen",
"selectOption": "Option auswählen",
"requiredOption": "Diese Option ist erforderlich.",
"requiredOption": "Einstellung dieser Option ist erforderlich",
"unsupportedOption": "Dieser Vorgang ist nicht unterstützt.",
"requiredOptionNull": "Die folgenden Optionen müssen gesetzt sein:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Προσαρμοσμένη τιμή",
"setToNull": "Ορισμός σε null",
"nullValue": "Αυτή η επιλογή είναι κενή",
"resetOptionsTooltip": "Επαναφορά επιλογών τροποποιήσεων",
"viewTitle": "Επιλογές τροποποιήσεων",
"saveOptions": "Αποθήκευση",
"addOptions": "Προσθήκη επιλογών",
"deselectPatch": "Αποεπιλέξτε τροποποιήσεις",
"unselectPatch": "Αποεπιλογή patch",
"tooltip": "Περισσότερες επιλογές εισόδου",
"selectFilePath": "Επιλογή τοποθεσίας αρχείου",
"selectFolder": "Επιλογή φακέλου",
"selectOption": "Επιλογή ρύθμισης",
"requiredOption": "Αυτή η επιλογή απαιτείται",
"requiredOption": "Απαιτείται ρύθμιση αυτής της επιλογής",
"unsupportedOption": "Αυτή η επιλογή δεν υποστηρίζεται",
"requiredOptionNull": "Πρέπει να οριστούν οι παρακάτω επιλογές:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Valor personalizado",
"setToNull": "Establecer a nulo",
"nullValue": "Este valor de opción es nulo actualmente",
"resetOptionsTooltip": "Restablecer a los valores por defecto",
"viewTitle": "Configuración\\ndel parche",
"saveOptions": "Guardar configuración",
"addOptions": "Agregar configuración",
"deselectPatch": "Deseleccionar parche",
"unselectPatch": "Deseleccionar parche",
"tooltip": "Más opciones de entrada",
"selectFilePath": "Seleccionar ruta del archivo",
"selectFolder": "Seleccionar carpeta",
"selectOption": "Seleccionar opción",
"requiredOption": "Esta opción es requerida",
"requiredOption": "La configuración de esta opción es necesaria",
"unsupportedOption": "Esta opción no es compatible",
"requiredOptionNull": "Tenés que configurar las siguientes opciones:\\n\\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Valor personalizado",
"setToNull": "Establecer a nulo",
"nullValue": "Este valor de opción es nulo actualmente",
"resetOptionsTooltip": "Restablecer las opciones de parche",
"viewTitle": "Opciones de parche",
"saveOptions": "Guardar",
"addOptions": "Añadir opciones",
"deselectPatch": "Deseleccionar parche",
"unselectPatch": "Deseleccionar parche",
"tooltip": "Más opciones de entrada",
"selectFilePath": "Seleccionar ruta del archivo",
"selectFolder": "Seleccionar carpeta",
"selectOption": "Seleccionar opción",
"requiredOption": "Esta opción es necesaria",
"requiredOption": "La configuración de esta opción es necesaria",
"unsupportedOption": "Esta opción no está disponible",
"requiredOptionNull": "Hay que establecer las siguientes opciones:\n\n${options}"
},

View File

@@ -41,7 +41,7 @@
"downloadConsentDialogTitle": "¿Descargar archivos necesarios?",
"downloadConsentDialogText": "ReVanced Manager necesita descargar los archivos necesarios para funcionar correctamente.",
"downloadConsentDialogText2": "Esto te conectará a ${url}.",
"checkUpdateDialogTitle": "¿Buscar actualizaciones?",
"checkUpdateDialogTitle": "¿Comprobar actualizaciones?",
"checkUpdateDialogText": "¿Quieres que ReVanced Manager compruebe si hay actualizaciones automáticamente?",
"notificationTitle": "Actualización descargada",
"notificationText": "Toca para instalar la actualización",
@@ -91,14 +91,14 @@
},
"appSelectorView": {
"viewTitle": "Seleccionar una aplicación",
"searchBarHint": "Buscar app",
"searchBarHint": "Buscar aplicación",
"storageButton": "Almacenamiento",
"selectFromStorageButton": "Seleccionar desde almacenamiento",
"errorMessage": "No se puede usar la aplicación seleccionada",
"downloadToast": "La función de descarga aún no está disponible",
"requireSuggestedAppVersionDialogText": "La versión de la aplicación que has seleccionado no coincide con la versión sugerida, lo que podría causar errores inesperados. Por favor, utiliza la versión sugerida.\n\nVersión seleccionada: ${selected}\nVersión sugerida: ${suggested}\n\nPara continuar de todas formas, desactiva \"Requerir versión sugerida de la aplicación\" en la configuración.",
"featureNotAvailable": "Característica no implementada",
"featureNotAvailableText": "Esta aplicación es un APK dividido y solo puede ser parcheada e instalada de forma fiable mediante el montaje con permisos de root. Sin embargo, puedes parchear e instalar un APK completo seleccionándolo del almacenamiento."
"featureNotAvailableText": "Esta aplicación es una APK dividida y solo puede ser parcheada e instalada confiablemente al montar con permisos de root. Sin embargo, puedes parchear e instalar una APK completa seleccionándola en el almacenamiento."
},
"patchesSelectorView": {
"viewTitle": "Selecciona parches",
@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Valor personalizado",
"setToNull": "Establecer a nulo",
"nullValue": "Este valor de opción es nulo actualmente",
"resetOptionsTooltip": "Restablecer las opciones de parche",
"viewTitle": "Opciones de parche",
"saveOptions": "Guardar",
"addOptions": "Añadir opciones",
"deselectPatch": "Deseleccionar parche",
"unselectPatch": "Deseleccionar parche",
"tooltip": "Más opciones de entrada",
"selectFilePath": "Selecciona la ruta del archivo",
"selectFolder": "Selecciona la carpeta",
"selectOption": "Seleccionar opción",
"requiredOption": "Esta opción es requerida",
"requiredOption": "La configuración de esta opción es necesaria",
"unsupportedOption": "Esta opción no está disponible",
"requiredOptionNull": "Hay que configurar las siguientes opciones:\n\n${options}"
},
@@ -196,7 +198,7 @@
"enablePatchesSelectionLabel": "Permitir cambiar la selección de parches",
"enablePatchesSelectionHint": "No prevenir la selección o deselección de parches",
"enablePatchesSelectionWarningText": "Cambiar la selección de parches puede cauar problemas inespereados.\n\n¿Quieres activarlo de todas formas?",
"disablePatchesSelectionWarningText": "Estás a punto de desactivar cambiar la selección de parches.\nLa selección predeterminada de parches se restaura.\n\n¿Deshabilitar de todos modos?",
"disablePatchesSelectionWarningText": "Estás a punto de desactivar cambiar la selección de los parches.\nLa selección predeterminada de los parches se restaurada.\n\n¿Desactivar de todos modos?",
"autoUpdatePatchesLabel": "Actualizar parches automáticamente",
"autoUpdatePatchesHint": "Actualiza los parches a la última versión automáticamente",
"showUpdateDialogLabel": "Mostrar diálogo de actualización",
@@ -252,7 +254,7 @@
"keystoreSelectorErrorMessage": "No se puede utilizar el archivo de repositorio de claves seleccionado"
},
"appInfoView": {
"widgetTitle": "Informacion de la applicacion",
"widgetTitle": "Informacion de la aplicación",
"openButton": "Abrir",
"uninstallButton": "Desinstalar",
"unmountButton": "Desmontar",

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Oma arvo",
"setToNull": "Aseta tyhjäksi",
"nullValue": "Tämä valinta arvo on tällä hetkellä nolla",
"resetOptionsTooltip": "Palauta paikkausasetukset",
"viewTitle": "Paikkausasetukset",
"saveOptions": "Tallenna",
"addOptions": "Lisää asetuksia",
"deselectPatch": "Poista paikkauksen valinta",
"unselectPatch": "Peru korjauksen valinta",
"tooltip": "Enemmän syöteasetuksia",
"selectFilePath": "Valitse tiedostosijainti",
"selectFolder": "Valitse kansio",
"selectOption": "Valitse asetus",
"requiredOption": "Tämä asetus vaaditaan",
"requiredOption": "Tämän asetuksen asettaminen on pakollinen",
"unsupportedOption": "Tätä asetusta ei tueta",
"requiredOptionNull": "Seuraavat asetukset on määritettävä:\n\n${options}"
},

View File

@@ -29,7 +29,7 @@
"updatesSubtitle": "Mises à jour",
"patchedSubtitle": "Applications patchées",
"changeLaterSubtitle": "Vous pouvez changer cela dans les paramètres ultérieurement.",
"noUpdates": "Aucune mise à jour n'est disponible",
"noUpdates": "Aucune mise à jour disponible",
"WIP": "Bientôt disponible...",
"noInstallations": "Aucune application patchée installée",
"installUpdate": "Continuer à installer la mise à jour ?",
@@ -51,7 +51,7 @@
"errorDownloadMessage": "Impossible de télécharger la mise à jour",
"errorInstallMessage": "Impossible d'installer la mise à jour",
"noConnection": "Aucune connexion internet",
"updatesDisabled": "La mise à jour d'une application patchée est actuellement désactivée. Repatchez l'application à nouveau."
"updatesDisabled": "La mise à jour d'une application patchée est actuellement désactivée. Repatchez l'application."
},
"applicationItem": {
"infoButton": "Info"
@@ -82,8 +82,8 @@
"patchSelectorCard": {
"widgetTitle": "Sélectionner les patchs",
"widgetTitleSelected": "Patchs sélectionnés",
"widgetSubtitle": "Choisissez d'abord une application",
"widgetEmptySubtitle": "Aucun patch n'est sélectionné"
"widgetSubtitle": "Sélectionnez d'abord une application",
"widgetEmptySubtitle": "Aucun patch sélectionné"
},
"socialMediaCard": {
"widgetTitle": "Réseaux sociaux",
@@ -118,24 +118,26 @@
},
"patchOptionsView": {
"customValue": "Valeur personnalisée",
"setToNull": "Définir à NULL",
"nullValue": "Cette valeur d'option est actuellement nulle",
"resetOptionsTooltip": "Réinitialiser les options de patch",
"viewTitle": "Options de patch",
"saveOptions": "Enregistrer",
"addOptions": "Ajouter des options",
"deselectPatch": "Désélectionner tous les patchs",
"unselectPatch": "Désélectionner le patch",
"tooltip": "Plus d'options d'entrée",
"selectFilePath": "Sélectionner l'emplacement du fichier",
"selectFolder": "Sélectionner le dossier",
"selectOption": "Sélectionner une option",
"requiredOption": "Cette option est obligatoire",
"unsupportedOption": "Cette option n'est pas supportée",
"requiredOption": "Définir cette option est nécessaire",
"unsupportedOption": "Cette option n'est pas prise en charge",
"requiredOptionNull": "Les options suivantes doivent être définies :\n\n${options}"
},
"patchItem": {
"unsupportedDialogText": "Sélectionner ce patch pourrait entrainer des erreurs dans la modification.\n\nVersion de l'application : ${packageVersion}\nVersions supportées :\n${supportedVersions}",
"unsupportedPatchVersion": "Le patch n'est pas supporté pour cette version de l'application.",
"unsupportedRequiredOption": "Ce patch contient une option requise qui n'est pas supporté par cette application",
"patchesChangeWarningDialogText": "Il est recommandé d'utiliser les patchs par défaut ainsi que les options. Leur modification peut entraîner des problèmes inattendus.\n\nVous aurez besoin d'activer \"Autoriser la modification de la sélection du patch\" dans les paramètres avant de modifier toute sélection de patch.",
"unsupportedDialogText": "Sélectionner ce patch peut entrainer des erreurs dans la modification.\n\nVersion de l'application : ${packageVersion}\nVersions prises en charge :\n${supportedVersions}",
"unsupportedPatchVersion": "Le patch n'est pas pris en charge pour cette version de l'application.",
"unsupportedRequiredOption": "Ce patch contient une option requise qui n'est pas prise en charge par cette application",
"patchesChangeWarningDialogText": "Il est recommandé d'utiliser les patchs et options par défaut. Leur modification peut entraîner des problèmes inattendus.\n\nVous aurez besoin d'activer « Autoriser la modification de la sélection de patchs » dans les paramètres avant de modifier toute sélection de patchs.",
"patchesChangeWarningDialogButton": "Utiliser la sélection par défaut"
},
"installerView": {
@@ -193,7 +195,7 @@
"contributorsHint": "Liste des contributeurs de ReVanced",
"logsLabel": "Partager les journaux",
"logsHint": "Partager les logs de ReVanced Manager",
"enablePatchesSelectionLabel": "Autoriser la modification de la sélection du patch",
"enablePatchesSelectionLabel": "Autoriser la modification de la sélection de patchs",
"enablePatchesSelectionHint": "Ne pas empêcher la sélection ou la désélection des patchs",
"enablePatchesSelectionWarningText": "Le changement de sélection par défaut des patchs peut causer des problèmes inattendus \n\nActiver quand même?",
"disablePatchesSelectionWarningText": "Vous êtes sur le point de désactiver le changement de sélection par défaut des patchs.\nLa sélection par défaut des patchs sera restaurée.\n\nDésactiver quand même ?",

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Valore personalizzato",
"setToNull": "Imposta a nullo",
"nullValue": "Questo valore di opzione è al momento nullo",
"resetOptionsTooltip": "Ripristina opzioni patch",
"viewTitle": "Opzioni patch",
"saveOptions": "Salva",
"addOptions": "Aggiungi opzioni",
"deselectPatch": "Deseleziona patch",
"unselectPatch": "Deseleziona patch",
"tooltip": "Più opzioni di input",
"selectFilePath": "Seleziona percorso file",
"selectFolder": "Seleziona cartella",
"selectOption": "Seleziona opzione",
"requiredOption": "Questa opzione è richiesta",
"requiredOption": "L'impostazione di questa opzione è obbligatoria",
"unsupportedOption": "Questa opzione non è supportata",
"requiredOptionNull": "È necessario impostare le seguenti opzioni:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "カスタム値",
"setToNull": "null に設定",
"nullValue": "このオプション値は現在nullです",
"resetOptionsTooltip": "パッチ設定をリセット",
"viewTitle": "パッチ設定",
"saveOptions": "保存",
"addOptions": "オプションを追加",
"deselectPatch": "パッチの選択を解除",
"unselectPatch": "パッチの選択を解除",
"tooltip": "他の入力オプション",
"selectFilePath": "ファイルパスを選択",
"selectFolder": "フォルダーを選択",
"selectOption": "オプションを選択",
"requiredOption": "このオプションは必須です",
"requiredOption": "このオプションを設定する必要があります",
"unsupportedOption": "この設定はサポートされていません",
"requiredOptionNull": "以下のオプションを設定する必要があります:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Aangepaste waarde",
"setToNull": "Zet op nul",
"nullValue": "Deze optiewaarde is momenteel leeg",
"resetOptionsTooltip": "Reset patch opties",
"viewTitle": "Patch opties",
"saveOptions": "Opslaan",
"addOptions": "Opties toevoegen",
"deselectPatch": "Deselecteer patch",
"unselectPatch": "Patch deselecteren",
"tooltip": "Meer invoeropties",
"selectFilePath": "Bestandspad selecteren",
"selectFolder": "Map selecteren",
"selectOption": "Selecteer optie",
"requiredOption": "Deze optie is vereist",
"requiredOption": "Het instellen van deze optie is vereist",
"unsupportedOption": "Deze optie wordt niet ondersteund",
"requiredOptionNull": "De volgende opties moeten worden ingesteld:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Niestandardowa wartość",
"setToNull": "Ustaw na zerowy",
"nullValue": "Ta opcja jest obecnie pusta",
"resetOptionsTooltip": "Zresetuj opcje od łatek",
"viewTitle": "Opcje łatek",
"saveOptions": "Zapisz",
"addOptions": "Dodaj opcje",
"deselectPatch": "Odznacz łatkę",
"unselectPatch": "Odznacz łatkę",
"tooltip": "Więcej opcji wejściowych",
"selectFilePath": "Wybierz ścieżkę pliku",
"selectFolder": "Wybierz folder",
"selectOption": "Wybierz opcję",
"requiredOption": "Ta opcja jest wymagana",
"requiredOption": "Ustawienie tej opcji jest wymagane",
"unsupportedOption": "Ta opcja nie jest wspierana",
"requiredOptionNull": "Należy ustawić następujące opcje:\n\n${options}"
},

View File

@@ -27,7 +27,7 @@
"refreshSuccess": "Atualizado com sucesso",
"widgetTitle": "Painel",
"updatesSubtitle": "Atualizações",
"patchedSubtitle": "Aplicativos patcheados",
"patchedSubtitle": "Aplicativos patcheados ",
"changeLaterSubtitle": "Você pode ajustar essa opção nas configurações mais tarde.",
"noUpdates": "Nenhuma atualização encontrada",
"WIP": "Trabalho em progresso...",
@@ -40,7 +40,7 @@
"updateDialogText": "Uma nova atualização está disponível para ${file}.\n\nA versão atualmente instalada é a ${version}.",
"downloadConsentDialogTitle": "Baixar os arquivos necessários?",
"downloadConsentDialogText": "O ReVanced Manager precisará baixar os arquivos necessários para funcionar corretamente.",
"downloadConsentDialogText2": "Isso irá te conectar em ${url}.",
"downloadConsentDialogText2": "Isso vai conectar você em ${url}.",
"checkUpdateDialogTitle": "Procurar atualizações?",
"checkUpdateDialogText": "Você quer que o ReVanced Manager procure atualizações automaticamente?",
"notificationTitle": "Atualização baixada",
@@ -71,9 +71,9 @@
"requiredOptionDialogText": "Algumas opções de patch tiveram que ser definidas."
},
"appSelectorCard": {
"widgetTitle": "Selecione um aplicativo",
"widgetTitleSelected": "Aplicativo selecionado",
"widgetSubtitle": "Nenhum aplicativo selecionado",
"widgetTitle": "Selecione um app",
"widgetTitleSelected": "App selecionado",
"widgetSubtitle": "Nenhum app selecionado",
"noAppsLabel": "Nenhum aplicativo foi encontrado",
"currentVersion": "Atual",
"suggestedVersion": "Sugeridos/sugestões",
@@ -96,7 +96,7 @@
"selectFromStorageButton": "Selecionar no armazenamento",
"errorMessage": "Não foi possível usar o app selecionado",
"downloadToast": "A função de download não está disponível no momento",
"requireSuggestedAppVersionDialogText": "The version of the app you have selected does not match the suggested version which can lead to unexpected issues. Please use the suggested version.\n\nSelected version: ${selected}\nSuggested version: ${suggested}\n\nTo continue anyway, disable \"Require suggested app version\" in the settings.",
"requireSuggestedAppVersionDialogText": "A versão do app selecionada não corresponde à versão sugerida, o que pode causar problemas inesperados. Por favor, use a versão sugerida.\n\nVersão selecionada: ${selected} \nVersão sugerida: ${suggested}\n\nPara continuar mesmo assim, desative \"Exigir versão sugerida do app\" nas configurações.",
"featureNotAvailable": "Recurso não implementado",
"featureNotAvailableText": "Este aplicativo é um APK dividido e só pode ser patcheado e instalado de forma confiável ao ser montado com permissões de root. No entanto, você pode patchear e instalar um APK completo ao selecioná-lo do armazenamento."
},
@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Valor personalizado",
"resetOptionsTooltip": "Redefinir opções de patch",
"setToNull": "Definir como nulo",
"nullValue": "Atualmente, este valor de opção é nulo",
"resetOptionsTooltip": "Redefinir as opções de patch",
"viewTitle": "Opções de patch",
"saveOptions": "Salvar",
"addOptions": "Adicionar opções",
"deselectPatch": "Deselecionar patch",
"unselectPatch": "Desmarque o patch",
"tooltip": "Mais opções de entrada",
"selectFilePath": "Selecione o caminho do arquivo",
"selectFolder": "Selecione a pasta",
"selectOption": "Selecione uma opção",
"requiredOption": "Essa opção é necessária",
"requiredOption": "Definir esta opção é necessário",
"unsupportedOption": "Essa opção não é suportada",
"requiredOptionNull": "As seguintes opções precisam ser definidas:\n\n${options}"
},
@@ -178,7 +180,7 @@
"sourcesLabelHint": "Conigure as fontes alternativas para ReVanced Patches e ReVanced Integrations",
"sourcesIntegrationsLabel": "Fonte das integrações",
"useAlternativeSources": "Usar fontes alternativas",
"useAlternativeSourcesHint": "Use alternative sources for ReVanced Patches and ReVanced Integrations instead of the API",
"useAlternativeSourcesHint": "Use fontes alternativas para Patches do ReVanced e Integrações do ReVanced em vez da API",
"sourcesResetDialogTitle": "Redefinir",
"sourcesResetDialogText": "Você tem certeza que deseja redefinir as fontes para os valores padrão?",
"apiURLResetDialogText": "Are you sure you want to reset your API URL to its default value?",
@@ -199,15 +201,15 @@
"disablePatchesSelectionWarningText": "Você irá desabilitar a mudança da pré-seleção dos patches.\nA seleção padrão dos patches será restaurada.\n\nDesabilitar mesmo assim?",
"autoUpdatePatchesLabel": "Atualizar patches automaticamente",
"autoUpdatePatchesHint": "Atualize automaticamente os patches para a versão mais recente",
"showUpdateDialogLabel": "Show update dialog",
"showUpdateDialogHint": "Show a dialog when a new update is available",
"showUpdateDialogLabel": "Mostrar alerta de atualização",
"showUpdateDialogHint": "Mostra um aviso quando uma nova atualização estiver disponível",
"universalPatchesLabel": "Mostrar patches universais",
"universalPatchesHint": "Mostra todos os aplicativos e patches universais (pode deixar a lista de aplicativos mais lenta)",
"versionCompatibilityCheckLabel": "Verificar compatibilidade de versão",
"versionCompatibilityCheckHint": "Prevent selecting patches that are not compatible with the selected app version",
"requireSuggestedAppVersionLabel": "Requer a versão sugerida do app",
"requireSuggestedAppVersionHint": "Evite selecionar um aplicativo com uma versão diferente da sugerida",
"requireSuggestedAppVersionDialogText": "Selecionar um aplicativo que não seja a versão sugerida pode causar problemas inesperados.\n\nVocê quer prosseguir mesmo assim?",
"versionCompatibilityCheckHint": "Impedir a seleção de patches que não são compatíveis com a versão do app selecionado",
"requireSuggestedAppVersionLabel": "Exigir versão sugerida do app",
"requireSuggestedAppVersionHint": "Evite selecionar um app com uma versão diferente da sugerida",
"requireSuggestedAppVersionDialogText": "Selecionar um app que não seja a versão sugerida pode causar problemas inesperados.\n\nVocê quer prosseguir mesmo assim?",
"aboutLabel": "Sobre",
"snackbarMessage": "Copiado para a área de transferência",
"restartAppForChanges": "Reinicie o aplicativo para aplicar as mudanças",
@@ -221,16 +223,16 @@
"importPatchesLabel": "Importar seleção de patch",
"importPatchesHint": "Importar seleção de patch de um arquivo JSON",
"importedPatches": "Seleção de patch importada",
"resetStoredPatchesLabel": "Redefinir seleção de patch",
"resetStoredPatchesLabel": "Redefinir a seleção de patch",
"resetStoredPatchesHint": "Redefinir a seleção de patch armazenada",
"resetStoredPatchesDialogTitle": "Redefinir seleção de patch?",
"resetStoredPatchesDialogTitle": "Redefinir a seleção de patch?",
"resetStoredPatchesDialogText": "A seleção padrão de patches será restaurada.",
"resetStoredPatches": "A seleção de patch foi redefinida",
"resetStoredOptionsLabel": "Redefinir opções de patch",
"resetStoredOptionsHint": "Resetar todas as opções de patch",
"resetStoredOptionsDialogTitle": "Reset patch options?",
"resetStoredOptionsDialogText": "Resetting patch options will remove all saved options.",
"resetStoredOptions": "As opções foram resetadas",
"resetStoredOptionsLabel": "Redefinir as opções de patch",
"resetStoredOptionsHint": "Redefinir todas as opções de patch",
"resetStoredOptionsDialogTitle": "Redefinir as opções de patch?",
"resetStoredOptionsDialogText": "Redefinir as opções de patch vai remover todas as opções salvas.",
"resetStoredOptions": "As opções foram redefinidas",
"deleteLogsLabel": "Limpar registros",
"deleteLogsHint": "Delete collected ReVanced Manager logs",
"deletedLogs": "Registros apagados",
@@ -286,7 +288,7 @@
"status_failure_incompatible": "Instalação incompatível",
"status_failure_timeout": "Tempo limite de instalação",
"status_unknown": "Falha na instalação",
"mount_version_mismatch_description": "A instalação falhou devido ao aplicativo instalado ser uma versão diferente do aplicativo patcheado.\n\nInstale a versão do aplicativo que você está montando e tente novamente.",
"mount_version_mismatch_description": "A instalação falhou porque o app instalado era de uma versão diferente do app patcheado.\n\nInstale a versão do app que você está montando e tente de novo.",
"mount_no_root_description": "A instalação falhou devido ao acesso root não ter sido concedido.\n\nConceda acesso root ao Gerenciador ReVanced e tente novamente.",
"mount_missing_installation_description": "A instalação falhou devido ao aplicativo não patcheado não estar instalado neste dispositivo para ser montado sobre ele.\n\nInstale o aplicativo não patcheado antes de montar e tente novamente.",
"status_failure_timeout_description": "A instalação demorou muito para terminar.\n\nVocê gostaria de tentar novamente?",
@@ -296,7 +298,7 @@
"status_failure_conflict_description": "A instalação foi impedida por uma instalação existente do aplicativo.\n\nDesinstale o aplicativo instalado e tente novamente",
"status_failure_blocked_description": "A instalação foi bloqueada por ${packageName}.\n\nAjuste suas configurações de segurança e tente novamente.",
"install_failed_verification_failure_description": "A instalação falhou devido a um problema de verificação.\n\nAjuste suas configurações de segurança e tente novamente.",
"install_failed_version_downgrade_description": "A instalação falhou devido ao aplicativo patcheado ser uma versão mais baixa que o aplicativo instalado.\n\nDesinstale o aplicativo e tente novamente.",
"install_failed_version_downgrade_description": "A instalação falhou porque o app patcheado era uma versão mais baixa que o app instalado.\n\nDesinstalar o app e tentar de novo?",
"status_unknown_description": "A instalação falhou por um motivo desconhecido. Por favor, tente novamente."
}
}

View File

@@ -1,7 +1,7 @@
{
"okButton": "OK",
"cancelButton": "Cancelar",
"dismissButton": "Dispensar",
"dismissButton": "Ignorar",
"quitButton": "Sair",
"updateButton": "Atualizar",
"enabledLabel": "Ativado",
@@ -27,11 +27,11 @@
"refreshSuccess": "Atualizado com sucesso",
"widgetTitle": "Painel de Controlo",
"updatesSubtitle": "Atualizações",
"patchedSubtitle": "Aplicações Modificadas",
"patchedSubtitle": "Apps patcheados",
"changeLaterSubtitle": "Podes modificar esta definição mais tarde.",
"noUpdates": "Nenhuma atualização disponível",
"WIP": "Trabalho em progresso...",
"noInstallations": "Nenhuma aplicação modificada instalada",
"noInstallations": "Nenhum app patcheado instalado",
"installUpdate": "Continuar para instalar a atualização?",
"updateSheetTitle": "Atualizar o ReVanced Manager",
"updateDialogTitle": "Nova atualização disponível",
@@ -51,7 +51,7 @@
"errorDownloadMessage": "Não é possível transferir a atualização",
"errorInstallMessage": "Não foi possível instalar a atualização",
"noConnection": "Sem ligação à Internet",
"updatesDisabled": "Atualizar uma aplicação modificada está atualmente desabilitado. Volta a modificar a aplicação."
"updatesDisabled": "A atualização de um app patcheado está desativada no momento. Repatch o app de novo."
},
"applicationItem": {
"infoButton": "Informação"
@@ -98,7 +98,7 @@
"downloadToast": "A função de transferência não está disponível",
"requireSuggestedAppVersionDialogText": "A versão da aplicação que selecionaste não corresponde à versão sugerida, o que pode levar a problemas inesperados. Utiliza a versão recomendada.\n\nVersão selecionada: ${selected}\nVersão recomendada: ${suggested}\n\nPara continuar na mesma, desactive a opção \"Exigir a versão recomendada da aplicação\" nas definições.",
"featureNotAvailable": "Recurso não implementado",
"featureNotAvailableText": "Esta aplicação é um APK dividido e só pode ser modificado e instalado de forma fiável através da montagem com permissões root. No entanto, é possível corrigir e instalar um APK completo selecionando-o a partir do armazenamento."
"featureNotAvailableText": "Esse app é um APK dividido e só pode ser modificado e instalado de forma fiável através da montagem com permissões root. No entanto, é possível patchear e instalar um APK completo selecionando ele a partir do armazenamento."
},
"patchesSelectorView": {
"viewTitle": "Selecionar modificações",
@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Valor personalizado",
"setToNull": "Definir como nulo",
"nullValue": "Atualmente, este valor de opção é nulo",
"resetOptionsTooltip": "Reiniciar as opções da modificação",
"viewTitle": "Opções de modificação",
"saveOptions": "Guardar",
"addOptions": "Adicionar opções",
"deselectPatch": "Desselecionar modificação",
"unselectPatch": "Desmarque o patch",
"tooltip": "Mais opções de entrada",
"selectFilePath": "Selecionar caminho do arquivo",
"selectFolder": "Selecionar pasta",
"selectOption": "Seleccionar opção",
"requiredOption": "Esta opção é obrigatória",
"requiredOption": "Definir esta opção é necessário",
"unsupportedOption": "Esta opção não é suportada",
"requiredOptionNull": "As seguintes opções devem ser definidas:\n\n${options}"
},
@@ -145,13 +147,13 @@
"installButton": "Instalar",
"installRootType": "Montar",
"installNonRootType": "Normal",
"warning": "Desativa as atualizações automáticas da aplicação modificada para evitar problemas inesperados.",
"warning": "Desative as atualizações automáticas do app patcheado para evitar problemas inesperados.",
"pressBackAgain": "Pressione voltar novamente para cancelar",
"openButton": "Abrir",
"shareButton": "Partilhar ficheiro",
"notificationTitle": "O ReVanced Manager está a fazer as modificações",
"notificationText": "Toca para voltar ao instalador",
"exportApkButtonTooltip": "Exportar APK modificado",
"exportApkButtonTooltip": "Exportar APK patcheado",
"exportLogButtonTooltip": "Exportar registo",
"screenshotDetected": "Foi detetada uma captura de ecrã. Se estiver a tentar partilhar o registo, partilhe antes uma cópia de texto.\n\nCopiar o registo para a área de transferência?",
"copiedToClipboard": "Registo copiado para a área de transferência",
@@ -258,13 +260,13 @@
"unmountButton": "Desmontar",
"rootDialogTitle": "Erro",
"unmountDialogText": "Tens a certeza que queres remover as modificações desta aplicação?",
"uninstallDialogText": "Tens a certeza que queres desinstalar esta aplicação?",
"uninstallDialogText": "Tem certeza que quer desinstalar esse app?",
"rootDialogText": "A aplicação foi instalada com permissões de Super-Utilizador, mas atualmente o ReVanced Manager não tem permissões.\nPor favor, conceda permissões de Super-Utilizador primeiro.",
"packageNameLabel": "Nome do pacote",
"installTypeLabel": "Tipo de instalação",
"mountTypeLabel": "Montar",
"regularTypeLabel": "Normal",
"patchedDateLabel": "Data da Modificação",
"patchedDateLabel": "Data do patch",
"appliedPatchesLabel": "Modificações aplicadas",
"patchedDateHint": "${date} às ${time}",
"appliedPatchesHint": "${quantity} modificação/ões aplicada/s",
@@ -293,10 +295,10 @@
"status_failure_storage_description": "A instalação falhou devido ao armazenamento insuficiente.\n\nLiberta algum espaço e tenta novamente.",
"status_failure_invalid_description": "A instalação falhou devido ao facto da aplicação modificada ser inválida.\n\nDesinstalar a aplicação e tentar novamente?",
"status_failure_incompatible_description": "O aplicativo é incompatível com este dispositivo.\n\nEntre em contacto com o desenvolvedor da aplicação e peça suporte.",
"status_failure_conflict_description": "A instalação foi impedida por uma instalação existente da mesma aplicação.\n\nDesinstalar a aplicação instalada e tentar novamente?",
"status_failure_conflict_description": "A instalação foi impedida por uma instalação existente do app.\n\nDesinstalar o app instalado e tentar de novo?",
"status_failure_blocked_description": "A instalação foi bloqueada por ${packageName}.\n\nAjuste as suas definições de segurança e tenta novamente.",
"install_failed_verification_failure_description": "A instalação falhou por problemas de verificação.\n\nAjusta as tuas definições de segurança e tenta novamente.",
"install_failed_version_downgrade_description": "A instalação falhou devido ao facto da aplicação modificada ser uma versão inferior à da aplicação instalada.\n\nDesinstalar a aplicação e tentar novamente?",
"install_failed_version_downgrade_description": "A instalação falhou porque o app patcheado era uma versão inferior ao aplicativo instalado.\n\nDesinstalar o app e tentar de novo?",
"status_unknown_description": "A instalação falhou por razões desconhecidas. Por favor, tenta novamente."
}
}

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Valoare personalizată",
"setToNull": "Setează ca nul",
"nullValue": "Această valoare este în prezent nulă",
"resetOptionsTooltip": "Resetează opțiunile patch-ului",
"viewTitle": "Opțiuni patch",
"saveOptions": "Salvează",
"addOptions": "Adaugă opțiuni",
"deselectPatch": "Deselectați toate patch-urile",
"unselectPatch": "Deselectează patch-ul",
"tooltip": "Mai multe opțiuni de intrare",
"selectFilePath": "Selectați calea fișierului",
"selectFolder": "Selectați dosarul",
"selectOption": "Selectați opțiunea",
"requiredOption": "Această opțiune este necesară",
"requiredOption": "Setarea acestei opțiuni este necesară",
"unsupportedOption": "Această opțiune nu este acceptată",
"requiredOptionNull": "Următoarele opțiuni trebuie setate:\n\n${options}"
},

View File

@@ -118,11 +118,13 @@
},
"patchOptionsView": {
"customValue": "Пользовательское значение",
"setToNull": "Установить null",
"nullValue": "Значение этого параметра в настоящее время является нулевым",
"resetOptionsTooltip": "Сброс параметров патчей",
"viewTitle": "Параметры патчей",
"saveOptions": "Сохранить",
"addOptions": "Добавить параметры",
"deselectPatch": "Снять выделение с патча",
"unselectPatch": "Отменить выбор патча",
"tooltip": "Другие параметры ввода",
"selectFilePath": "Выберите путь к файлу",
"selectFolder": "Выберите папку",

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Anpassat värde",
"setToNull": "Sätt till noll",
"nullValue": "Detta alternativ värde är för närvarande noll",
"resetOptionsTooltip": "Återställ patchalternativ",
"viewTitle": "Patchalternativ",
"saveOptions": "Spara",
"addOptions": "Lägg till alternativ",
"deselectPatch": "Avmarkera patch",
"unselectPatch": "Avmarkera patch",
"tooltip": "Fler inmatningsalternativ",
"selectFilePath": "Välj filsökväg",
"selectFolder": "Välj mapp",
"selectOption": "Välj alternativ",
"requiredOption": "Detta alternativ är nödvändigt",
"requiredOption": "Inställning av detta alternativ krävs",
"unsupportedOption": "Detta alternativ stöds ej",
"requiredOptionNull": "Följande alternativ måste anges:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "Власне значення",
"setToNull": "Встановити в null",
"nullValue": "Значення опції в даний час null",
"resetOptionsTooltip": "Скинути параметри патчу",
"viewTitle": "Параметри патчу",
"saveOptions": "Зберегти",
"addOptions": "Додати параметри",
"deselectPatch": "Зняти вибір патчу",
"unselectPatch": "Скасувати вибір патча",
"tooltip": "Більше варіантів вводу",
"selectFilePath": "Оберіть шлях до файлу",
"selectFolder": "Оберіть теку",
"selectOption": "Вибрати параметр",
"requiredOption": "Цей параметр є обовязковим",
"requiredOption": "Встановлення цього параметра є обов'язковим",
"unsupportedOption": "Цей параметр не підтримується",
"requiredOptionNull": "Необхідно встановити наступні параметри:\n\n${options}"
},

View File

@@ -118,16 +118,18 @@
},
"patchOptionsView": {
"customValue": "自定义值",
"setToNull": "设置为为空",
"nullValue": "此选项值当前为空",
"resetOptionsTooltip": "重置补丁选项",
"viewTitle": "补丁选项",
"saveOptions": "保存",
"addOptions": "添加选项",
"deselectPatch": "取消选择补丁",
"unselectPatch": "取消选择补丁",
"tooltip": "更多输入选项",
"selectFilePath": "选择文件路径",
"selectFolder": "选择文件夹",
"selectOption": "选择选项",
"requiredOption": "必须填写此选项",
"requiredOption": "设置此选项是必需的",
"unsupportedOption": "不支持此选项",
"requiredOptionNull": "必须设置以下选项:\n\n${options}"
},

View File

@@ -6,12 +6,14 @@ import 'package:injectable/injectable.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/download_manager.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:synchronized/synchronized.dart';
@lazySingleton
class GithubAPI {
late final Dio _dio;
late final ManagerAPI _managerAPI = locator<ManagerAPI>();
late final DownloadManager _downloadManager = locator<DownloadManager>();
final Map<String, Lock> _lockMap = {};
Future<void> initialize(String repoUrl) async {
_dio = _downloadManager.initDio(repoUrl);
@@ -21,11 +23,21 @@ class GithubAPI {
await _downloadManager.clearAllCache();
}
Future<Response> _dioGetSynchronously(String path) async {
// Create a new Lock for each path
if (!_lockMap.containsKey(path)) {
_lockMap[path] = Lock();
}
return _lockMap[path]!.synchronized(() async {
return await _dio.get(path);
});
}
Future<Map<String, dynamic>?> getLatestRelease(
String repoName,
) async {
try {
final response = await _dio.get(
final response = await _dioGetSynchronously(
'/repos/$repoName/releases/latest',
);
return response.data;
@@ -37,36 +49,27 @@ class GithubAPI {
}
}
Future<Map<String, dynamic>?> getLatestManagerRelease(
String repoName,
) async {
Future<String?> getManagerChangelogs() async {
try {
final response = await _dio.get(
'/repos/$repoName/releases',
final response = await _dioGetSynchronously(
'/repos/${_managerAPI.defaultManagerRepo}/releases?per_page=50',
);
final Map<String, dynamic> releases = response.data[0];
int updates = 0;
final buffer = StringBuffer();
final String currentVersion =
await _managerAPI.getCurrentManagerVersion();
while (response.data[updates]['tag_name'] != currentVersion) {
updates++;
}
for (int i = 1; i < updates; i++) {
if (response.data[i]['prerelease']) {
for (final release in response.data) {
if (release['tag_name'] == currentVersion) {
if (buffer.isEmpty) {
buffer.writeln(release['body']);
}
break;
}
if (release['prerelease']) {
continue;
}
releases.update(
'body',
(value) =>
value +
'\n' +
'# ' +
response.data[i]['tag_name'] +
'\n' +
response.data[i]['body'],
);
buffer.writeln(release['body']);
}
return releases;
return buffer.toString();
} on Exception catch (e) {
if (kDebugMode) {
print(e);
@@ -87,7 +90,7 @@ class GithubAPI {
url,
);
}
final response = await _dio.get(
final response = await _dioGetSynchronously(
'/repos/$repoName/releases/tags/$version',
);
final Map<String, dynamic>? release = response.data;

View File

@@ -31,7 +31,6 @@ class ManagerAPI {
final String cliRepo = 'revanced-cli';
late SharedPreferences _prefs;
List<Patch> patches = [];
List<Option> modifiedOptions = [];
List<Option> options = [];
Patch? selectedPatch;
BuildContext? ctx;

View File

@@ -39,7 +39,8 @@ class HomeViewModel extends BaseViewModel {
List<PatchedApplication> patchedInstalledApps = [];
String _currentManagerVersion = '';
String _currentPatchesVersion = '';
String? _latestManagerVersion = '';
String? latestManagerVersion;
String? latestPatchesVersion;
File? downloadedApk;
Future<void> initialize(BuildContext context) async {
@@ -50,7 +51,6 @@ class HomeViewModel extends BaseViewModel {
await forceRefresh(context);
return;
}
_latestManagerVersion = await _managerAPI.getLatestManagerVersion();
_currentPatchesVersion = await _managerAPI.getCurrentPatchesVersion();
if (_managerAPI.showUpdateDialog() && await hasManagerUpdates()) {
showUpdateDialog(context, false);
@@ -131,21 +131,21 @@ class HomeViewModel extends BaseViewModel {
if (!_managerAPI.releaseBuild) {
return false;
}
_latestManagerVersion =
latestManagerVersion =
await _managerAPI.getLatestManagerVersion() ?? _currentManagerVersion;
if (_latestManagerVersion != _currentManagerVersion) {
if (latestManagerVersion != _currentManagerVersion) {
return true;
}
return false;
}
Future<bool> hasPatchesUpdates() async {
final String? latestVersion = await _managerAPI.getLatestPatchesVersion();
if (latestVersion != null) {
latestPatchesVersion = await _managerAPI.getLatestPatchesVersion();
if (latestPatchesVersion != null) {
try {
final int latestVersionInt =
int.parse(latestVersion.replaceAll(RegExp('[^0-9]'), ''));
int.parse(latestPatchesVersion!.replaceAll(RegExp('[^0-9]'), ''));
final int currentVersionInt =
int.parse(_currentPatchesVersion.replaceAll(RegExp('[^0-9]'), ''));
return latestVersionInt > currentVersionInt;
@@ -475,12 +475,14 @@ class HomeViewModel extends BaseViewModel {
);
}
Future<Map<String, dynamic>?> getLatestManagerRelease() {
return _githubAPI.getLatestManagerRelease(_managerAPI.defaultManagerRepo);
Future<String?> getManagerChangelogs() {
return _githubAPI.getManagerChangelogs();
}
Future<Map<String, dynamic>?> getLatestPatchesRelease() {
return _githubAPI.getLatestRelease(_managerAPI.defaultPatchesRepo);
Future<String?> getLatestPatchesChangelog() async {
final release =
await _githubAPI.getLatestRelease(_managerAPI.defaultPatchesRepo);
return release?['body'];
}
Future<String?> getLatestPatchesReleaseTime() {

View File

@@ -37,79 +37,35 @@ class PatchOptionsView extends StatelessWidget {
color: Theme.of(context).textTheme.titleLarge!.color,
),
),
actions: [
IconButton(
onPressed: () {
model.resetOptions();
},
icon: const Icon(
Icons.history,
),
tooltip: t.patchOptionsView.resetOptionsTooltip,
),
],
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
for (final Option option in model.visibleOptions)
for (final Option option in model.modifiedOptions)
if (option.valueType == 'String' ||
option.valueType == 'Int')
IntAndStringPatchOption(
patchOption: option,
removeOption: (option) {
model.removeOption(option);
},
onChanged: (value, option) {
model.modifyOptions(value, option);
},
model: model,
)
else if (option.valueType == 'Boolean')
BooleanPatchOption(
patchOption: option,
removeOption: (option) {
model.removeOption(option);
},
onChanged: (value, option) {
model.modifyOptions(value, option);
},
model: model,
)
else if (option.valueType == 'StringArray' ||
option.valueType == 'IntArray' ||
option.valueType == 'LongArray')
IntStringLongListPatchOption(
patchOption: option,
removeOption: (option) {
model.removeOption(option);
},
onChanged: (value, option) {
model.modifyOptions(value, option);
},
model: model,
)
else
UnsupportedPatchOption(
patchOption: option,
),
if (model.visibleOptions.length !=
model.options.length) ...[
const SizedBox(
height: 8,
),
FilledButton(
onPressed: () {
model.showAddOptionDialog(context);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.add),
Text(t.patchOptionsView.addOptions),
],
),
),
],
const SizedBox(
height: 80,
),

View File

@@ -5,7 +5,6 @@ import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
import 'package:stacked/stacked.dart';
class PatchOptionsViewModel extends BaseViewModel {
@@ -14,7 +13,7 @@ class PatchOptionsViewModel extends BaseViewModel {
locator<PatcherViewModel>().selectedApp!.packageName;
List<Option> options = [];
List<Option> savedOptions = [];
List<Option> visibleOptions = [];
List<Option> modifiedOptions = [];
Future<void> initialize() async {
options = getDefaultOptions();
@@ -28,36 +27,18 @@ class PatchOptionsViewModel extends BaseViewModel {
savedOptions.add(savedOption);
}
}
if (savedOptions.isNotEmpty) {
visibleOptions = [
...savedOptions,
...options.where(
(option) =>
option.required &&
!savedOptions.any((sOption) => sOption.key == option.key),
),
];
} else {
visibleOptions = [
...options.where((option) => option.required),
];
}
}
void addOption(Option option) {
visibleOptions.add(option);
notifyListeners();
}
void removeOption(Option option) {
visibleOptions.removeWhere((vOption) => vOption.key == option.key);
notifyListeners();
modifiedOptions = [
...savedOptions,
...options.where(
(option) => !savedOptions.any((sOption) => sOption.key == option.key),
),
];
}
bool saveOptions(BuildContext context) {
final List<Option> requiredNullOptions = [];
for (final Option option in options) {
if (!visibleOptions.any((vOption) => vOption.key == option.key)) {
if (modifiedOptions.any((mOption) => mOption.key == option.key)) {
_managerAPI.clearPatchOption(
selectedApp,
_managerAPI.selectedPatch!.name,
@@ -65,7 +46,7 @@ class PatchOptionsViewModel extends BaseViewModel {
);
}
}
for (final Option option in visibleOptions) {
for (final Option option in modifiedOptions) {
if (option.required && option.value == null) {
requiredNullOptions.add(option);
} else {
@@ -98,11 +79,8 @@ class PatchOptionsViewModel extends BaseViewModel {
required: option.required,
key: option.key,
);
visibleOptions[visibleOptions
.indexWhere((vOption) => vOption.key == option.key)] = modifiedOption;
_managerAPI.modifiedOptions
.removeWhere((mOption) => mOption.key == option.key);
_managerAPI.modifiedOptions.add(modifiedOption);
modifiedOptions.removeWhere((mOption) => mOption.key == option.key);
modifiedOptions.add(modifiedOption);
}
List<Option> getDefaultOptions() {
@@ -122,93 +100,11 @@ class PatchOptionsViewModel extends BaseViewModel {
return defaultOptions;
}
void resetOptions() {
_managerAPI.modifiedOptions.clear();
visibleOptions =
getDefaultOptions().where((option) => option.required).toList();
notifyListeners();
}
Future<void> showAddOptionDialog(BuildContext context) async {
await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
t.patchOptionsView.addOptions,
),
Text(
'',
style: TextStyle(
fontSize: 16,
color: Theme.of(context).colorScheme.onSecondaryContainer,
),
),
],
),
actions: [
FilledButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(t.cancelButton),
),
],
contentPadding: const EdgeInsets.all(8),
content: Wrap(
spacing: 14,
runSpacing: 14,
children: options
.where(
(option) =>
!visibleOptions.any((vOption) => vOption.key == option.key),
)
.map((e) {
return CustomCard(
padding: const EdgeInsets.all(4),
backgroundColor: Theme.of(context).colorScheme.surface,
onTap: () {
addOption(e);
Navigator.pop(context);
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
e.title,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).colorScheme.onSurface,
),
),
const SizedBox(height: 4),
Text(
e.description,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurface,
),
),
],
),
),
],
),
),
);
}).toList(),
),
),
);
}
dynamic getDefaultValue(Option patchOption) => _managerAPI.options
.firstWhere(
(option) => option.key == patchOption.key,
)
.value;
}
Future<void> showRequiredOptionNullDialog(
@@ -248,7 +144,7 @@ Future<void> showRequiredOptionNullDialog(
PatchesSelectorViewModel().showPatchesChangeDialog(context);
}
},
child: Text(t.patchOptionsView.deselectPatch),
child: Text(t.patchOptionsView.unselectPatch),
),
FilledButton(
onPressed: () {

View File

@@ -61,7 +61,6 @@ class PatchesSelectorViewModel extends BaseViewModel {
void navigateToPatchOptions(List<Option> setOptions, Patch patch) {
_managerAPI.options = setOptions;
_managerAPI.selectedPatch = patch;
_managerAPI.modifiedOptions.clear();
_navigationService.navigateToPatchOptionsView();
}

View File

@@ -27,6 +27,7 @@ class SManageSources extends BaseViewModel {
return showDialog(
context: context,
builder: (context) => AlertDialog(
scrollable: true,
title: Row(
children: <Widget>[
Expanded(
@@ -39,75 +40,73 @@ class SManageSources extends BaseViewModel {
),
],
),
content: SingleChildScrollView(
child: Column(
children: <Widget>[
TextField(
controller: _orgPatSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: Icon(
Icons.extension_outlined,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.orgPatchesLabel,
hintText: patchesRepo.split('/')[0],
content: Column(
children: <Widget>[
TextField(
controller: _orgPatSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: Icon(
Icons.extension_outlined,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.orgPatchesLabel,
hintText: patchesRepo.split('/')[0],
),
const SizedBox(height: 8),
// Patches repository's name
TextField(
controller: _patSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: const Icon(
Icons.extension_outlined,
color: Colors.transparent,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.sourcesPatchesLabel,
hintText: patchesRepo.split('/')[1],
),
const SizedBox(height: 8),
// Patches repository's name
TextField(
controller: _patSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: const Icon(
Icons.extension_outlined,
color: Colors.transparent,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.sourcesPatchesLabel,
hintText: patchesRepo.split('/')[1],
),
const SizedBox(height: 8),
// Integrations owner's name
TextField(
controller: _orgIntSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: Icon(
Icons.merge_outlined,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.orgIntegrationsLabel,
hintText: integrationsRepo.split('/')[0],
),
const SizedBox(height: 8),
// Integrations owner's name
TextField(
controller: _orgIntSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: Icon(
Icons.merge_outlined,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.orgIntegrationsLabel,
hintText: integrationsRepo.split('/')[0],
),
const SizedBox(height: 8),
// Integrations repository's name
TextField(
controller: _intSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: const Icon(
Icons.merge_outlined,
color: Colors.transparent,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.sourcesIntegrationsLabel,
hintText: integrationsRepo.split('/')[1],
),
const SizedBox(height: 8),
// Integrations repository's name
TextField(
controller: _intSourceController,
autocorrect: false,
onChanged: (value) => notifyListeners(),
decoration: InputDecoration(
icon: const Icon(
Icons.merge_outlined,
color: Colors.transparent,
),
border: const OutlineInputBorder(),
labelText: t.settingsView.sourcesIntegrationsLabel,
hintText: integrationsRepo.split('/')[1],
),
const SizedBox(height: 20),
Text(t.settingsView.sourcesUpdateNote),
],
),
),
const SizedBox(height: 20),
Text(t.settingsView.sourcesUpdateNote),
],
),
actions: <Widget>[
TextButton(

View File

@@ -3,6 +3,7 @@
import 'package:flutter/material.dart';
import 'package:language_code/language_code.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/toast.dart';
@@ -10,8 +11,10 @@ import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
final _settingViewModel = SettingsViewModel();
final _navigationService = NavigationService();
class SUpdateLanguage extends BaseViewModel {
final Toast _toast = locator<Toast>();
@@ -108,10 +111,9 @@ class SUpdateLanguage extends BaseViewModel {
child: Text(t.cancelButton),
),
TextButton(
onPressed: () {
// TODO(nullcube): Translation will not update until we refresh the page.
onPressed: () async {
updateLocale(selectedLanguageCode.value.languageTag);
Navigator.of(context).pop();
await _navigationService.navigateToNavigationView();
},
child: Text(t.okButton),
),

View File

@@ -14,6 +14,7 @@ class UpdateConfirmationSheet extends StatelessWidget {
final bool isPatches;
final bool changelog;
@override
Widget build(BuildContext context) {
final HomeViewModel model = locator<HomeViewModel>();
@@ -25,100 +26,99 @@ class UpdateConfirmationSheet extends StatelessWidget {
builder: (_, scrollController) => SingleChildScrollView(
controller: scrollController,
child: SafeArea(
child: FutureBuilder<Map<String, dynamic>?>(
future: !isPatches
? model.getLatestManagerRelease()
: model.getLatestPatchesRelease(),
builder: (_, snapshot) {
if (!snapshot.hasData) {
return const SizedBox(
height: 300,
child: Center(
child: CircularProgressIndicator(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!changelog)
Padding(
padding: const EdgeInsets.only(
top: 40.0,
left: 24.0,
right: 24.0,
bottom: 20.0,
),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!changelog)
Padding(
padding: const EdgeInsets.only(
top: 40.0,
left: 24.0,
right: 24.0,
bottom: 20.0,
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isPatches
? t.homeView.updatePatchesSheetTitle
: t.homeView.updateSheetTitle,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4.0),
Row(
children: [
Icon(
Icons.new_releases_outlined,
color:
Theme.of(context).colorScheme.secondary,
),
const SizedBox(width: 8.0),
Text(
isPatches
? t.homeView.updatePatchesSheetTitle
: t.homeView.updateSheetTitle,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
? model.latestPatchesVersion ?? 'Unknown'
: model.latestManagerVersion ?? 'Unknown',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color:
Theme.of(context).colorScheme.secondary,
),
),
const SizedBox(height: 4.0),
Row(
children: [
Icon(
Icons.new_releases_outlined,
color: Theme.of(context)
.colorScheme
.secondary,
),
const SizedBox(width: 8.0),
Text(
snapshot.data!['tag_name'] ?? 'Unknown',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.colorScheme
.secondary,
),
),
],
),
],
),
),
FilledButton(
onPressed: () {
Navigator.of(context).pop();
isPatches
? model.updatePatches(context)
: model.updateManager(context);
},
child: Text(t.updateButton),
),
],
],
),
),
),
Padding(
padding: const EdgeInsets.only(
top: 12.0,
left: 24.0,
bottom: 12.0,
),
child: Text(
t.homeView.updateChangelogTitle,
style: TextStyle(
fontSize: changelog ? 24 : 20,
fontWeight: FontWeight.w500,
color:
Theme.of(context).colorScheme.onSecondaryContainer,
FilledButton(
onPressed: () {
Navigator.of(context).pop();
isPatches
? model.updatePatches(context)
: model.updateManager(context);
},
child: Text(t.updateButton),
),
),
],
),
Container(
),
Padding(
padding: const EdgeInsets.only(
top: 12.0,
left: 24.0,
bottom: 12.0,
),
child: Text(
t.homeView.updateChangelogTitle,
style: TextStyle(
fontSize: changelog ? 24 : 20,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.onSecondaryContainer,
),
),
),
FutureBuilder<String?>(
future: !isPatches
? model.getManagerChangelogs()
: model.getLatestPatchesChangelog(),
builder: (_, snapshot) {
if (!snapshot.hasData) {
return Padding(
padding: EdgeInsets.only(top: changelog ? 96 : 24),
child: const Center(
child: CircularProgressIndicator(),
),
);
}
return Container(
margin: const EdgeInsets.symmetric(horizontal: 24.0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondaryContainer,
@@ -139,12 +139,12 @@ class UpdateConfirmationSheet extends StatelessWidget {
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.all(20.0),
data: snapshot.data!['body'] ?? '',
data: snapshot.data ?? '',
),
),
],
);
},
);
},
),
],
),
),
),

View File

@@ -3,19 +3,18 @@ import 'package:flutter/services.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/ui/views/patch_options/patch_options_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
class BooleanPatchOption extends StatelessWidget {
const BooleanPatchOption({
super.key,
required this.patchOption,
required this.removeOption,
required this.onChanged,
required this.model,
});
final Option patchOption;
final void Function(Option option) removeOption;
final void Function(dynamic value, Option option) onChanged;
final PatchOptionsViewModel model;
@override
Widget build(BuildContext context) {
@@ -30,88 +29,94 @@ class BooleanPatchOption extends StatelessWidget {
value: value ?? false,
onChanged: (bool value) {
patchOptionValue.value = value;
onChanged(value, patchOption);
model.modifyOptions(value, patchOption);
},
);
},
),
),
patchOption: patchOption,
removeOption: (Option option) {
removeOption(option);
},
patchOptionValue: patchOptionValue,
model: model,
);
}
}
class IntAndStringPatchOption extends StatelessWidget {
class IntAndStringPatchOption extends StatefulWidget {
const IntAndStringPatchOption({
super.key,
required this.patchOption,
required this.removeOption,
required this.onChanged,
required this.model,
});
final Option patchOption;
final void Function(Option option) removeOption;
final void Function(dynamic value, Option option) onChanged;
final PatchOptionsViewModel model;
@override
State<IntAndStringPatchOption> createState() =>
_IntAndStringPatchOptionState();
}
class _IntAndStringPatchOptionState extends State<IntAndStringPatchOption> {
ValueNotifier? patchOptionValue;
String getKey() {
if (patchOptionValue!.value != null && widget.patchOption.values != null) {
final List values = widget.patchOption.values!.entries
.where((e) => e.value == patchOptionValue!.value)
.toList();
if (values.isNotEmpty) {
return values.first.key;
}
}
return '';
}
@override
Widget build(BuildContext context) {
final ValueNotifier patchOptionValue = ValueNotifier(patchOption.value);
String getKey() {
if (patchOption.value != null && patchOption.values != null) {
final List values = patchOption.values!.entries
.where((e) => e.value == patchOption.value)
.toList();
if (values.isNotEmpty) {
return values.first.key;
}
}
return '';
}
patchOptionValue ??= ValueNotifier(widget.patchOption.value);
return PatchOption(
widget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFieldForPatchOption(
value: patchOption.value,
values: patchOption.values,
optionType: patchOption.valueType,
selectedKey: getKey(),
onChanged: (value) {
patchOptionValue.value = value;
onChanged(value, patchOption);
},
),
ValueListenableBuilder(
valueListenable: patchOptionValue,
builder: (context, value, child) {
if (patchOption.required && value == null) {
return Column(
widget: ValueListenableBuilder(
valueListenable: patchOptionValue!,
builder: (context, value, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFieldForPatchOption(
value: value,
patchOption: widget.patchOption,
selectedKey: getKey(),
onChanged: (value) {
patchOptionValue!.value = value;
widget.model.modifyOptions(value, widget.patchOption);
},
),
if (value == null)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 8),
Text(
t.patchOptionsView.requiredOption,
widget.patchOption.required
? t.patchOptionsView.requiredOption
: t.patchOptionsView.nullValue,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
color: widget.patchOption.required
? Theme.of(context).colorScheme.error
: Theme.of(context)
.colorScheme
.onSecondaryContainer
.withOpacity(0.6),
),
),
],
);
} else {
return const SizedBox();
}
},
),
],
),
],
);
},
),
patchOption: patchOption,
removeOption: (Option option) {
removeOption(option);
},
patchOption: widget.patchOption,
patchOptionValue: patchOptionValue!,
model: widget.model,
);
}
}
@@ -120,13 +125,11 @@ class IntStringLongListPatchOption extends StatelessWidget {
const IntStringLongListPatchOption({
super.key,
required this.patchOption,
required this.removeOption,
required this.onChanged,
required this.model,
});
final Option patchOption;
final void Function(Option option) removeOption;
final void Function(dynamic value, Option option) onChanged;
final PatchOptionsViewModel model;
@override
Widget build(BuildContext context) {
@@ -172,8 +175,7 @@ class IntStringLongListPatchOption extends StatelessWidget {
final e = values[index];
return TextFieldForPatchOption(
value: e.toString(),
values: patchOption.values,
optionType: type,
patchOption: patchOption,
selectedKey: value.length > 1 ? '' : getKey(e),
showDropdown: index == 0,
onChanged: (newValue) {
@@ -205,13 +207,13 @@ class IntStringLongListPatchOption extends StatelessWidget {
}
}
patchOptionValue.value = List.from(values);
onChanged(values, patchOption);
model.modifyOptions(values, patchOption);
},
removeValue: () {
patchOptionValue.value = List.from(patchOptionValue.value)
..removeAt(index);
values.removeAt(index);
onChanged(values, patchOption);
model.modifyOptions(values, patchOption);
},
);
},
@@ -231,7 +233,7 @@ class IntStringLongListPatchOption extends StatelessWidget {
List.from(patchOptionValue.value)..add(0);
values.add(0);
}
onChanged(values, patchOption);
model.modifyOptions(values, patchOption);
},
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -254,9 +256,8 @@ class IntStringLongListPatchOption extends StatelessWidget {
},
),
patchOption: patchOption,
removeOption: (Option option) {
removeOption(option);
},
patchOptionValue: patchOptionValue,
model: model,
);
}
}
@@ -282,7 +283,8 @@ class UnsupportedPatchOption extends StatelessWidget {
),
),
patchOption: patchOption,
removeOption: (_) {},
patchOptionValue: ValueNotifier(null),
model: PatchOptionsViewModel(),
);
}
}
@@ -292,15 +294,18 @@ class PatchOption extends StatelessWidget {
super.key,
required this.widget,
required this.patchOption,
required this.removeOption,
required this.patchOptionValue,
required this.model,
});
final Widget widget;
final Option patchOption;
final void Function(Option option) removeOption;
final ValueNotifier patchOptionValue;
final PatchOptionsViewModel model;
@override
Widget build(BuildContext context) {
final defaultValue = model.getDefaultValue(patchOption);
return Padding(
padding: const EdgeInsets.all(8.0),
child: CustomCard(
@@ -337,11 +342,24 @@ class PatchOption extends StatelessWidget {
],
),
),
if (!patchOption.required)
IconButton(
onPressed: () => removeOption(patchOption),
icon: const Icon(Icons.delete),
),
ValueListenableBuilder(
valueListenable: patchOptionValue,
builder: (context, value, child) {
if (defaultValue != patchOptionValue.value) {
return IconButton(
onPressed: () {
patchOptionValue.value = defaultValue;
model.modifyOptions(
defaultValue,
patchOption,
);
},
icon: const Icon(Icons.history),
);
}
return const SizedBox();
},
),
],
),
const SizedBox(height: 4),
@@ -360,17 +378,15 @@ class TextFieldForPatchOption extends StatefulWidget {
const TextFieldForPatchOption({
super.key,
required this.value,
required this.values,
required this.patchOption,
this.removeValue,
required this.onChanged,
required this.optionType,
required this.selectedKey,
this.showDropdown = true,
});
final String? value;
final Map<String, dynamic>? values;
final String optionType;
final Option patchOption;
final String selectedKey;
final bool showDropdown;
final void Function()? removeValue;
@@ -388,20 +404,19 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
@override
Widget build(BuildContext context) {
final bool isStringOption = widget.optionType.contains('String');
final bool isArrayOption = widget.optionType.contains('Array');
selectedKey ??= widget.selectedKey;
controller.text = !isStringOption &&
isArrayOption &&
selectedKey == '' &&
(widget.value != null && widget.value.toString().startsWith('['))
? ''
: widget.value ?? '';
final bool isStringOption = widget.patchOption.valueType.contains('String');
final bool isArrayOption = widget.patchOption.valueType.contains('Array');
selectedKey = selectedKey == '' ? selectedKey : widget.selectedKey;
final bool isValueArray = widget.value?.startsWith('[') ?? false;
final bool shouldResetValue =
!isStringOption && isArrayOption && selectedKey == '' && isValueArray;
controller.text = shouldResetValue ? '' : widget.value ?? '';
defaultValue ??= controller.text;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.showDropdown && (widget.values?.isNotEmpty ?? false))
if (widget.showDropdown &&
(widget.patchOption.values?.isNotEmpty ?? false))
DropdownButton<String>(
style: const TextStyle(
fontSize: 16,
@@ -410,11 +425,12 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
dropdownColor: Theme.of(context).colorScheme.secondaryContainer,
isExpanded: true,
value: selectedKey,
items: widget.values!.entries
items: widget.patchOption.values!.entries
.map(
(e) => DropdownMenuItem(
value: e.key,
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
text: e.key,
style: TextStyle(
@@ -427,7 +443,7 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
TextSpan(
text: ' ${e.value}',
style: TextStyle(
fontSize: 14,
fontSize: 16,
color: Theme.of(context)
.colorScheme
.onSecondaryContainer
@@ -447,9 +463,7 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
t.patchOptionsView.customValue,
style: TextStyle(
fontSize: 16,
color: Theme.of(context)
.colorScheme
.onSecondaryContainer,
color: Theme.of(context).colorScheme.onSecondaryContainer,
),
),
),
@@ -459,9 +473,11 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
controller.text = defaultValue!;
widget.onChanged(controller.text);
} else {
controller.text = widget.values![value].toString();
controller.text = widget.patchOption.values![value].toString();
widget.onChanged(
isArrayOption ? widget.values![value] : controller.text,
isArrayOption
? widget.patchOption.values![value]
: controller.text,
);
}
setState(() {
@@ -472,9 +488,9 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
if (selectedKey == '')
TextFormField(
inputFormatters: [
if (widget.optionType.contains('Int'))
if (widget.patchOption.valueType.contains('Int'))
FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
if (widget.optionType.contains('Long'))
if (widget.patchOption.valueType.contains('Long'))
FilteringTextInputFormatter.allow(RegExp(r'^[0-9]*\.?[0-9]*')),
],
controller: controller,
@@ -487,31 +503,36 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
return [
if (isArrayOption)
PopupMenuItem(
value: t.remove,
value: 'remove',
child: Text(t.remove),
),
if (isStringOption) ...[
PopupMenuItem(
value: t.patchOptionsView.selectFilePath,
value: 'file',
child: Text(t.patchOptionsView.selectFilePath),
),
PopupMenuItem(
value: t.patchOptionsView.selectFolder,
value: 'folder',
child: Text(t.patchOptionsView.selectFolder),
),
],
if (!widget.patchOption.required)
PopupMenuItem(
value: 'null',
child: Text(t.patchOptionsView.setToNull),
),
];
},
onSelected: (String selection) async {
switch (selection) {
case 'patchOptionsView.selectFilePath':
case 'file':
final String? result = await FlutterFileDialog.pickFile();
if (result != null) {
controller.text = result;
widget.onChanged(controller.text);
}
break;
case 'patchOptionsView.selectFolder':
case 'folder':
final DirectoryLocation? result =
await FlutterFileDialog.pickDirectory();
if (result != null) {
@@ -522,6 +543,10 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
case 'remove':
widget.removeValue!();
break;
case 'null':
controller.text = '';
widget.onChanged(null);
break;
}
},
),

763
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,6 @@
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"semantic-release": "^23.0.7"
"semantic-release": "^23.0.8"
}
}

View File

@@ -323,9 +323,9 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "560d21c4148b53933313573e7eafca0b0eb9aadf"
resolved-ref: "560d21c4148b53933313573e7eafca0b0eb9aadf"
url: "https://github.com/BenjaminHalko/flutter_background"
ref: "900a81501f1f0e4996fe9e2cbf55f1ea8df08b49"
resolved-ref: "900a81501f1f0e4996fe9e2cbf55f1ea8df08b49"
url: "https://github.com/validcube/flutter_background"
source: git
version: "1.2.0"
flutter_cache_manager:
@@ -499,10 +499,10 @@ packages:
dependency: "direct main"
description:
name: injectable
sha256: fb722c86cf8233008e4db41c696a6145721f45dc8aeba91103e3128c3d63c9c6
sha256: "3c8355a29d11ff28c0311bed754649761f345ef7a13ff66a714380954af51226"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.2"
injectable_generator:
dependency: "direct dev"
description:
@@ -571,18 +571,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
url: "https://pub.dev"
source: hosted
version: "10.0.4"
version: "10.0.5"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.5"
leak_tracker_testing:
dependency: transitive
description:
@@ -644,18 +644,18 @@ packages:
dependency: transitive
description:
name: material_color_utilities
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.8.0"
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
sha256: "25dfcaf170a0190f47ca6355bdd4552cb8924b430512ff0cafb8db9bd41fe33b"
url: "https://pub.dev"
source: hosted
version: "1.12.0"
version: "1.14.0"
mime:
dependency: transitive
description:
@@ -732,10 +732,10 @@ packages:
dependency: transitive
description:
name: path_provider_foundation
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.4.0"
path_provider_linux:
dependency: transitive
description:
@@ -989,10 +989,11 @@ packages:
skeletons:
dependency: "direct main"
description:
name: skeletons
sha256: "5b2d08ae7f908ee1f7007ca99f8dcebb4bfc1d3cb2143dec8d112a5be5a45c8f"
url: "https://pub.dev"
source: hosted
path: "."
ref: "326fbb4223ac4d8a6301cc2f16b6112ecd308c71"
resolved-ref: "326fbb4223ac4d8a6301cc2f16b6112ecd308c71"
url: "https://github.com/Ofceab-Studio/skeletons"
source: git
version: "0.0.3"
sky_engine:
dependency: transitive
@@ -1147,10 +1148,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
sha256: "2419f20b0c8677b2d67c8ac4d1ac7372d862dc6c460cdbb052b40155408cd794"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
version: "0.7.1"
timeago:
dependency: "direct main"
description:
@@ -1211,10 +1212,10 @@ packages:
dependency: transitive
description:
name: url_launcher_ios
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89"
url: "https://pub.dev"
source: hosted
version: "6.2.5"
version: "6.3.0"
url_launcher_linux:
dependency: transitive
description:
@@ -1227,10 +1228,10 @@ packages:
dependency: transitive
description:
name: url_launcher_macos
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.2.0"
url_launcher_platform_interface:
dependency: transitive
description:
@@ -1275,10 +1276,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
sha256: "7475cb4dd713d57b6f7464c0e13f06da0d535d8b2067e188962a59bac2cf280b"
url: "https://pub.dev"
source: hosted
version: "14.2.1"
version: "14.2.2"
wakelock_plus:
dependency: "direct main"
description:

View File

@@ -4,7 +4,7 @@ homepage: https://github.com/ReVanced/revanced-manager
publish_to: 'none'
version: 1.21.0-dev.3+101800019
version: 1.21.0-dev.5+101800021
environment:
sdk: '>=3.0.0 <4.0.0'
@@ -26,9 +26,9 @@ dependencies:
flutter:
sdk: flutter
flutter_background:
git: # remove once https://github.com/JulianAssmann/flutter_background/pull/79 is merged
url: https://github.com/BenjaminHalko/flutter_background
ref: 560d21c4148b53933313573e7eafca0b0eb9aadf # Branch: specify-namespace
git: # remove once https://github.com/JulianAssmann/flutter_background/pull/83 is merged
url: https://github.com/validcube/flutter_background
ref: 900a81501f1f0e4996fe9e2cbf55f1ea8df08b49 # Branch: specify-namespace
flutter_cache_manager: ^3.3.2
flutter_file_dialog: ^3.0.2
flutter_local_notifications: ^17.1.0
@@ -39,7 +39,7 @@ dependencies:
font_awesome_flutter: ^10.7.0
google_fonts: ^6.2.1
injectable: ^2.4.0
intl: ^0.18.1
intl: 0.19.0
json_annotation: ^4.9.0
language_code: ^0.5.3+2
logcat:
@@ -59,7 +59,10 @@ dependencies:
ref: 1a1616ac91e16cd1f3dd170a81febf27ffce3587 # Branch: master
share_plus: ^9.0.0
shared_preferences: ^2.2.3
skeletons: ^0.0.3
skeletons:
git: # remove once https://github.com/badjio/skeletons/pull/11 is merged
url: https://github.com/Ofceab-Studio/skeletons
ref: 326fbb4223ac4d8a6301cc2f16b6112ecd308c71 # Branch: master
slang: ^3.30.2
slang_flutter: ^3.30.0
stacked: ^3.4.2