Compare commits

..

24 Commits

Author SHA1 Message Date
d3d8f89d53 chore: bump version to 0.0.55 2023-02-10 05:34:10 +01:00
3d8aee09c3 build: bump patcher version 2023-02-10 05:33:50 +01:00
318cd87a9a feat(style): use the correct m3 theming (partially) (#680) 2023-02-07 15:46:29 +03:00
5d63d5c2d3 feat: potentially fix apps disappearing when update is available (#674) 2023-01-31 15:42:00 +03:00
7d347fccc6 build: bump version to v0.0.54 2023-01-30 15:57:06 +03:00
a54ca799b9 feat: update rules of analysis_options.yaml. and solved all problems (#665)
* update rules of analysis_options.yaml. and solved all problems

* refactor: fix remaining problems

---------

Co-authored-by: Ushie <ushiekane@gmail.com>
2023-01-30 15:35:06 +03:00
f5bc1a996f feat: remove select all icon from searchbar (#669) 2023-01-30 15:13:25 +03:00
8591bc4d01 chore: update cr_file_saver (#668)
Co-authored-by: Ushie <github@ushie.dev>
2023-01-30 15:11:31 +03:00
40888c07f3 feat: fix overflow text & separate version from patch name (#666)
* chore: Add note

* feat: Fix overflow text & seperate version from patch title

* feat: Fix overflow text & separate version from patch name
2023-01-30 15:07:25 +03:00
1c965c3788 refactor: cleanup remember patches feature (#630) 2023-01-30 15:03:55 +03:00
4df690c2a2 feat: monochrome icon + predictive back gesture (#591)
* Add monochrome icon

* Enable predictive back gesture

* feat: monochrome icon + predictive back gesture
2023-01-30 01:23:56 +03:00
d6abb61e2b ci(build): update workflow actions (#662)
- Bump upload-artifact to v3

- Update crowdin/github-action according to new versioning format [https://github.com/crowdin/github-action/releases/tag/v1.6.0]
2023-01-29 23:56:13 +03:00
3434c862e9 feat: improve note [skip ci] 2023-01-28 07:49:57 +01:00
ea8af926fa chore: update broken documentation link (#659)
* chorus: fix invalid documentation link

* chorus: Update broken documentation link
2023-01-22 16:18:41 +03:00
c3df48174c feat: warn user for selecting all patches (#649) 2023-01-21 16:37:28 +01:00
f1e60f96c4 chore: bump version to 0.0.53 2023-01-18 22:07:00 +01:00
cdd852678b build: bump patcher version 2023-01-18 22:07:00 +01:00
bf518b5467 bump: kotlin gradle plugin 2023-01-18 22:07:00 +01:00
ffd53fab26 feat: clarify acknowledgement label (#608) 2023-01-14 20:23:49 +03:00
5aad7dad35 chore: fix incorrect wording 2023-01-12 21:03:11 +01:00
b1c1a9f4e1 feat: stale development notice [skip ci] 2023-01-07 14:24:43 +01:00
d847a8e0b2 build: bump version to v0.0.52 2022-12-19 01:39:48 +01:00
9668730b5d build: bump patcher version (#610) 2022-12-19 01:35:58 +01:00
dc049cf26a New Crowdin translations by Github Action (#601)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-16 22:41:26 +03:00
91 changed files with 1799 additions and 809 deletions

View File

@ -116,5 +116,5 @@ body:
required: true required: true
- label: I filled out all of the requested information in this issue properly. - label: I filled out all of the requested information in this issue properly.
required: true required: true
- label: The issue is related solely to the ReVanced Manager - label: The issue is related solely to the ReVanced Manager and not related to patching errors or patches
required: true required: true

View File

@ -49,7 +49,7 @@ jobs:
- name: Add version to APK - name: Add version to APK
run: mv ${{ steps.sign_apk.outputs.signedFile }} revanced-manager-${{ env.RELEASE_VERSION }}.apk run: mv ${{ steps.sign_apk.outputs.signedFile }} revanced-manager-${{ env.RELEASE_VERSION }}.apk
- name: Upload APK - name: Upload APK
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: revanced-manager-${{ env.RELEASE_VERSION }} name: revanced-manager-${{ env.RELEASE_VERSION }}
path: revanced-manager-${{ env.RELEASE_VERSION }}.apk path: revanced-manager-${{ env.RELEASE_VERSION }}.apk

View File

@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Crowdin - name: Crowdin
uses: crowdin/github-action@1.5.0 uses: crowdin/github-action@v1
with: with:
config: crowdin.yml config: crowdin.yml
upload_sources: true upload_sources: true

View File

@ -35,7 +35,7 @@ jobs:
- name: Add version to APK - name: Add version to APK
run: mv build/app/outputs/flutter-apk/app-release.apk revanced-manager-${{ env.RELEASE_VERSION }}.apk run: mv build/app/outputs/flutter-apk/app-release.apk revanced-manager-${{ env.RELEASE_VERSION }}.apk
- name: Upload APK - name: Upload APK
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: revanced-manager-${{ env.RELEASE_VERSION }} name: revanced-manager-${{ env.RELEASE_VERSION }}
path: revanced-manager-${{ env.RELEASE_VERSION }}.apk path: revanced-manager-${{ env.RELEASE_VERSION }}.apk

View File

@ -2,6 +2,8 @@
The official ReVanced Manager based on Flutter. The official ReVanced Manager based on Flutter.
> **Warning**: This repository currently has no active maintainer. For that reason, development is stale. Active development continues for [revanced-cli](https://github.com/revanced/revanced-cli). If you are interested in maintaining this repository, please let us know at manager@revanced.app.
## 🔽 Download ## 🔽 Download
To download the Alpha version of Manager, go [here](https://github.com/revanced/revanced-manager/releases/latest) and install the provided APK file. To download the Alpha version of Manager, go [here](https://github.com/revanced/revanced-manager/releases/latest) and install the provided APK file.
@ -28,7 +30,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-documentation/blob/main/docs/revanced-development/2_building_from_source.md) 3. Add your github token in gradle.properties like [this](https://github.com/revanced/revanced-manager/blob/docs/docs/5_building-from-source.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

@ -14,20 +14,149 @@ analyzer:
- lib/utils/env_class.g.dart - lib/utils/env_class.g.dart
linter: linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules: rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule - always_declare_return_types
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - require_trailing_commas
- always_put_control_body_on_new_line
# Additional information about this file can be found at - always_require_non_null_named_parameters
# https://dart.dev/guides/language/analysis-options - always_use_package_imports # we do this commonly
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_double_and_int_checks
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
- avoid_implementing_value_types
- avoid_init_to_null
- avoid_js_rounded_ints
- avoid_null_checks_in_equality_operators
- avoid_print
- avoid_redundant_argument_values
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_returning_null
- avoid_returning_null_for_future
- avoid_returning_null_for_void
- avoid_setters_without_getters
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_type_to_string
- avoid_types_as_parameter_names
- avoid_unnecessary_containers
- avoid_void_async
- avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
- cast_nullable_to_non_nullable
- close_sinks # not reliable enough
- control_flow_in_finally
- curly_braces_in_flow_control_structures
- depend_on_referenced_packages
- deprecated_consistency
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
- eol_at_end_of_file
- exhaustive_cases
- file_names
- flutter_style_todos
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
- library_private_types_in_public_api
- list_remove_unrelated_type
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_logic_in_create_state
- non_constant_identifier_names
- noop_primitive_operations
- null_check_on_nullable_type_parameter
- null_closures
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_contains
- prefer_equal_for_default_values
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
- prefer_mixin # Has false positives, see https://github.com/dart-lang/linter/issues/3018
- prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere
- prefer_null_aware_operators
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- provide_deprecation_message
- recursive_getters
- sized_box_for_whitespace
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
- type_init_formals
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unrelated_type_equality_checks
- unsafe_html
- use_build_context_synchronously
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_if_null_to_convert_nulls_to_bools
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_named_constants
- use_raw_strings
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_test_throws_matchers
- valid_regexps
- void_checks

View File

@ -71,7 +71,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// ReVanced // ReVanced
implementation "app.revanced:revanced-patcher:6.3.1" implementation "app.revanced:revanced-patcher:6.4.3"
// Signing & aligning // Signing & aligning
implementation("org.bouncycastle:bcpkix-jdk15on:1.70") implementation("org.bouncycastle:bcpkix-jdk15on:1.70")

View File

@ -17,7 +17,8 @@
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:largeHeap="true" android:largeHeap="true"
android:extractNativeLibs="true"> android:extractNativeLibs="true"
android:enableOnBackInvokedCallback="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@ -28,8 +29,7 @@
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<meta-data <meta-data
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"/>
/>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>

View File

@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@ -1,6 +1,6 @@
buildscript { buildscript {
ext.cronetVersion = '102.5005.125' ext.cronetVersion = '102.5005.125'
ext.kotlin_version = '1.7.20' ext.kotlin_version = '1.8.0'
repositories { repositories {
google() google()
mavenCentral() mavenCentral()

View File

@ -1,6 +1,7 @@
{ {
"okButton": "موافق", "okButton": "موافق",
"cancelButton": "إلغاء", "cancelButton": "إلغاء",
"updateButton": "تحديث",
"enabledLabel": "مفعّل", "enabledLabel": "مفعّل",
"disabledLabel": "معطّل", "disabledLabel": "معطّل",
"yesButton": "نعم", "yesButton": "نعم",
@ -21,7 +22,7 @@
"noInstallations": "لا توجد تطبيقات معدلة مثبتة", "noInstallations": "لا توجد تطبيقات معدلة مثبتة",
"installed": "مثبت", "installed": "مثبت",
"updateDialogTitle": "تحديث المدير", "updateDialogTitle": "تحديث المدير",
"updateDialogText": "هل أنت متأكد من رغبتك في تنزيل وتحديث ReVanced Manager ؟", "updateChangelogTitle": "سجل التغييرات",
"notificationTitle": "تم تنزيل التحديث", "notificationTitle": "تم تنزيل التحديث",
"notificationText": "أنقر لتثبيت التحديث", "notificationText": "أنقر لتثبيت التحديث",
"downloadingMessage": "جاري تحميل التحديث...", "downloadingMessage": "جاري تحميل التحديث...",
@ -81,13 +82,13 @@
"all": "الكل", "all": "الكل",
"none": "بدون", "none": "بدون",
"loadPatchesSelection": "تحميل التعديلات المحددة", "loadPatchesSelection": "تحميل التعديلات المحددة",
"noSavedPatches": "لا توجد تعديلات محفوظة للتطبيق المحدد\nاضغط على تم لحفظ التحديد الحالي", "noSavedPatches": "لا توجد تعديلات محفوظة للتطبيق المختار.\nاضغط على تم لحفظ الاختيار الحالي.",
"noPatchesFound": "لم يتم العثور على تعديلات للتطبيق المحدد", "noPatchesFound": "لم يتم العثور على تعديلات للتطبيق المحدد",
"selectAllPatchesWarningContent": "أنت على وشك تحديد جميع التعديلات، بما في ذلك التعديلات غير الموصى بها والتي يمكن أن تسبب سلوكاً غير مرغوب فيه." "selectAllPatchesWarningContent": "أنت على وشك تحديد جميع التعديلات، بما في ذلك التعديلات غير الموصى بها والتي يمكن أن تسبب سلوكاً غير مرغوب فيه."
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "قد يؤدي تحديد هذا التعديل إلى حدوث أخطاء في عملية التعديل.\n\nإصدار التطبيق: {packageVersion}\nالإصدارات المدعومة حالياً:\n{supportedVersions}", "unsupportedDialogText": "قد يؤدي تحديد هذا التعديل إلى حدوث أخطاء في عملية التعديل.\n\nإصدار التطبيق: {packageVersion}\nالإصدارات المدعومة حالياً:\n{supportedVersions}",
"unsupportedPatchVersion": "التعديل غير مدعوم لهذا الإصدار من التطبيق. مكن التعديل التجريبي في الإعدادات للمتابعة." "unsupportedPatchVersion": "التعديل غير مدعوم لهذا الإصدار من التطبيق. فعِّل خِيار التعديل التجريبي في الإعدادات للمتابعة."
}, },
"installerView": { "installerView": {
"widgetTitle": "المثبت", "widgetTitle": "المثبت",
@ -121,6 +122,7 @@
"englishOption": "الإنجليزية", "englishOption": "الإنجليزية",
"sourcesLabel": "المصادر", "sourcesLabel": "المصادر",
"sourcesLabelHint": "تكوين مصادرك المخصصة", "sourcesLabelHint": "تكوين مصادرك المخصصة",
"hostRepositoryLabel": "مستودع API",
"orgPatchesLabel": "تنظيم التعديلات", "orgPatchesLabel": "تنظيم التعديلات",
"sourcesPatchesLabel": "مصدر التعديلات", "sourcesPatchesLabel": "مصدر التعديلات",
"orgIntegrationsLabel": "تنظيم الدمج", "orgIntegrationsLabel": "تنظيم الدمج",
@ -135,6 +137,8 @@
"apiURLLabel": "رابط API", "apiURLLabel": "رابط API",
"apiURLHint": "تكوين رابط API الخاص بك", "apiURLHint": "تكوين رابط API الخاص بك",
"selectApiURL": "رابط API", "selectApiURL": "رابط API",
"experimentalUniversalPatchesLabel": "دعم التعديلات العامة التجريبية",
"experimentalUniversalPatchesHint": "عرض جميع التطبيقات المراد استخدامها مع التعديلات العامة، قد يكون تحميل قائمة التطبيقات أبطأ",
"experimentalPatchesLabel": "دعم التعديلات التجريبية", "experimentalPatchesLabel": "دعم التعديلات التجريبية",
"experimentalPatchesHint": "تمكين استخدام التعديلات غير المدعومة في أي إصدار للتطبيق", "experimentalPatchesHint": "تمكين استخدام التعديلات غير المدعومة في أي إصدار للتطبيق",
"enabledExperimentalPatches": "تم تمكين دعم التعديلات التجريبية", "enabledExperimentalPatches": "تم تمكين دعم التعديلات التجريبية",

200
assets/i18n/bg_BG.json Normal file
View File

@ -0,0 +1,200 @@
{
"okButton": "OK",
"cancelButton": "Отказ",
"updateButton": "Актуализация",
"enabledLabel": "Включено",
"disabledLabel": "Изключено",
"yesButton": "Да",
"noButton": "Не",
"warning": "Внимание",
"navigationView": {
"dashboardTab": "Табло за управление",
"patcherTab": "Пачър",
"settingsTab": "Настройки"
},
"homeView": {
"widgetTitle": "Табло за управление",
"updatesSubtitle": "Актуализации",
"patchedSubtitle": "Модифицирани приложения",
"updatesAvailable": "Налични актуализации",
"noUpdates": "Няма налични актуализации",
"WIP": "В процес на разработка...",
"noInstallations": "Няма инсталирани модифицирани приложения",
"installed": "Инсталирани",
"updateDialogTitle": "Обнови ReVanced Manager",
"updateChangelogTitle": "Списък с промени",
"notificationTitle": "Актуализацията е изтеглена",
"notificationText": "Натиснете за да инсталирате актуализациите",
"downloadingMessage": "Изтегляне на актуализация...",
"installingMessage": "Инсталиране на актуализация...",
"errorDownloadMessage": "Свалянето на актуализацията не беше успешно",
"errorInstallMessage": "Инсталирането на актуализацията не беше успешно",
"noConnection": "Няма връзка с интернет",
"updatesDisabled": "Актуализацията на модифицирани приложения в момента не работи. Модифицирайте приложението наново."
},
"applicationItem": {
"patchButton": "Модифицирай",
"infoButton": "Информация",
"changelogLabel": "Списък с промените"
},
"latestCommitCard": {
"loadingLabel": "Зареждане...",
"timeagoLabel": "Преди {time}",
"patcherLabel": "Пачър: ",
"managerLabel": "Мениджър: ",
"updateButton": "Обнови ReVanced Manager"
},
"patcherView": {
"widgetTitle": "Модификатор",
"patchButton": "Модифицирай",
"patchDialogText": "Избрали сте модификация за ресурси и разделен APK пакет, което може да доведе до грешки при пачването.\nСигурни ли сте, че искате да продължите?"
},
"appSelectorCard": {
"widgetTitle": "Изберете приложение",
"widgetTitleSelected": "Избрано приложение",
"widgetSubtitle": "Няма избрано приложение",
"noAppsLabel": "Няма намерени приложения",
"currentVersion": "Текуща",
"recommendedVersion": "Препоръчана",
"anyVersion": "всяка"
},
"patchSelectorCard": {
"widgetTitle": "Изберете модификации",
"widgetTitleSelected": "Избрани модификации",
"widgetSubtitle": "Първо изберете приложение",
"widgetEmptySubtitle": "Няма избрани модификации"
},
"socialMediaCard": {
"widgetTitle": "Социални мрежи",
"widgetSubtitle": "Открийте ни онлайн!"
},
"appSelectorView": {
"viewTitle": "Изберете приложение",
"searchBarHint": "Търсене на приложение",
"storageButton": "Хранилище",
"errorMessage": "Избраното приложение не може да се използва"
},
"patchesSelectorView": {
"viewTitle": "Изберете модификации",
"searchBarHint": "Търсене на модификации",
"doneButton": "Готово",
"recommended": "Препоръчано",
"all": "Всички",
"none": "Нито един",
"loadPatchesSelection": "Заредете избраните модификации",
"noSavedPatches": "Няма запазени модификации за избраното приложение.\nНатиснете Готово за да запазите текущия избор.",
"noPatchesFound": "Няма налични модификации за избраното приложение",
"selectAllPatchesWarningContent": "Напът сте да изберете всички патчове, дори и тези, които не са препоръчителни, а това може да доведе до нежелани промени."
},
"patchItem": {
"unsupportedDialogText": "Изборът на тази модификация може да доведе до грешки.\n\nВерсия: {packageVersion}\nПоддържани версии: {supportedVersions}",
"unsupportedPatchVersion": "Модификацията не се поддържа за тази версия на приложението. Активираите експерименталната опция в настройките за да продължите."
},
"installerView": {
"widgetTitle": "Инсталатор",
"installButton": "Инсталиране",
"installRootButton": "Инсталиране с root",
"openButton": "Отвори",
"shareButton": "Споделяне на файла",
"notificationTitle": "ReVanced Мениджър модифицира",
"notificationText": "Натиснете за да се върнете при инсталатора",
"shareApkMenuOption": "Споделяне на APK",
"exportApkMenuOption": "Експортиране на APK",
"shareLogMenuOption": "Сподели логовете",
"installErrorDialogTitle": "Грешка",
"installErrorDialogText1": "Инсталиране с root не е възможно с настоящият избор на модификации.\nМодифицирайте приложението наново или изберете опция без root.",
"installErrorDialogText2": "Инсталиране без root не е възможно с настоящият избор на модификации.\nМодифицирайте приложението наново или изберете опция с root, ако устройството ви има такъв.",
"installErrorDialogText3": "Инсталиране с root не е възможно понеже АРК файла бе избран от хранилището.\nИзберете инсталирано приложение или опция без root.",
"noExit": "Инсталаторът все още работи, не може да излезе..."
},
"settingsView": {
"widgetTitle": "Настройки",
"appearanceSectionTitle": "Облик",
"teamSectionTitle": "Екип",
"infoSectionTitle": "Информация",
"advancedSectionTitle": "Разширени",
"logsSectionTitle": "Логове",
"darkThemeLabel": "Тъмен режим",
"darkThemeHint": "Добре дошъл на тъмната страна",
"dynamicThemeLabel": "Материална Тема",
"dynamicThemeHint": "Насладете се на преживяване по-близо до устройството си",
"languageLabel": "Език",
"englishOption": "Английски",
"sourcesLabel": "Източници",
"sourcesLabelHint": "Конфигурирайте собствени източници",
"hostRepositoryLabel": "API на хранилището",
"orgPatchesLabel": "Организация на модификациите",
"sourcesPatchesLabel": "Източник на модификациите",
"orgIntegrationsLabel": "Организация на интеграциите",
"sourcesIntegrationsLabel": "Източник на интеграциите",
"sourcesResetDialogTitle": "Нулиране",
"sourcesResetDialogText": "Искате ли да възстановите източниците до стойностите им по подразбиране?",
"apiURLResetDialogText": "Искате ли да възстановите АРI URL до стойността му по подразбиране?",
"contributorsLabel": "Хора, които допринесоха",
"contributorsHint": "Списък с хората, допринесли за ReVanced",
"logsLabel": "Логове",
"logsHint": "Сподели логовете на мениджъра",
"apiURLLabel": "API URL",
"apiURLHint": "Конфигурирайте собствен API URL",
"selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "Поддръжка на експериментални модификации",
"experimentalUniversalPatchesHint": "Показване на всички приложения за използване с универсални модификации, зареждането на списъкът с приложения може да е по-бавно",
"experimentalPatchesLabel": "Поддръжка на експериментални модификации",
"experimentalPatchesHint": "Разрешете използването на неподдържани модификации във всяка версия на приложението",
"enabledExperimentalPatches": "Поддръжката на експериментални модификации е активирана",
"exportSectionTitle": "Импортиране и експортиране",
"aboutLabel": "Относно",
"snackbarMessage": "Копирано",
"sentryLabel": "Sentry логване",
"sentryHint": "Пращайте анонимни логове за да ни помогнете да подобрим ReVanced Manager",
"restartAppForChanges": "Рестартирайте приложението, за да се приложат промените",
"deleteKeystoreLabel": "Изтриване на keystore",
"deleteKeystoreHint": "Изтриване на keystore използван за подписване на приложението",
"deletedKeystore": "Keystore изтрит",
"deleteTempDirLabel": "Изтриване на временни файлове",
"deleteTempDirHint": "Изтриване на неизползвани временни файлове",
"deletedTempDir": "Временните файлове са изтрити",
"exportPatchesLabel": "Експортиране на избраните модификации",
"exportPatchesHint": "Експортиране на избраните модификации като JSON файл",
"exportedPatches": "Избраните модификации са експортирани",
"noExportFileFound": "Няма избрани модификации за експортиране",
"importPatchesLabel": "Импортиране на избраните модификации",
"importPatchesHint": "Импортиране на избраните модификации от JSON файл",
"importedPatches": "Избраните модификации са импортирани",
"resetStoredPatchesLabel": "Нулиране на модификациите",
"resetStoredPatchesHint": "Нулиране на избраните запазени модификации",
"resetStoredPatches": "Избраните модификации са нулирани",
"jsonSelectorErrorMessage": "Избраният JSON файл не може да се изплозва",
"deleteLogsLabel": "Изтриване на логовете",
"deleteLogsHint": "Изтриване на събраните логове на мениджърът",
"deletedLogs": "Логовете са изтрити"
},
"appInfoView": {
"widgetTitle": "Информация за приложението",
"openButton": "Отвори",
"uninstallButton": "Деинсталирай",
"patchButton": "Модифицирайте",
"unpatchButton": "Премахнете модификации",
"unpatchDialogText": "Сигурни ли сте че искате да премахнете модификациите?",
"rootDialogTitle": "Грешка",
"rootDialogText": "Приложението е инсталирано с superuser разрешения, но в момента ReVanced Manager няма разрешения. Моля, първо дайте superuser разрешения.",
"packageNameLabel": "Име на пакета",
"originalPackageNameLabel": "Оригинално име на пакета",
"installTypeLabel": "Тип инсталация",
"rootTypeLabel": "Вариант с root достъп",
"nonRootTypeLabel": "Без root",
"patchedDateLabel": "Дата на модификацията",
"patchedDateHint": "на {date} в {time}",
"appliedPatchesLabel": "Приложени модификации",
"appliedPatchesHint": "{quantity} приложени модификации",
"updateNotImplemented": "Тази функция все още не е внедрена"
},
"contributorsView": {
"widgetTitle": "Хора, които допринесоха",
"patcherContributors": "Допринесли към пачърът",
"patchesContributors": "Допринесли към модификациите",
"integrationsContributors": "Допринесли към интеграциите",
"cliContributors": "Допринесли към CLI",
"managerContributors": "Допринесли към мениджъра"
}
}

View File

@ -1,6 +1,7 @@
{ {
"okButton": "OK", "okButton": "OK",
"cancelButton": "Afbryd", "cancelButton": "Afbryd",
"updateButton": "Opdatér",
"enabledLabel": "Aktiveret", "enabledLabel": "Aktiveret",
"disabledLabel": "Deaktiveret", "disabledLabel": "Deaktiveret",
"yesButton": "Ja", "yesButton": "Ja",
@ -21,7 +22,7 @@
"noInstallations": "Ingen patchede apps installeret", "noInstallations": "Ingen patchede apps installeret",
"installed": "Installeret", "installed": "Installeret",
"updateDialogTitle": "Opdater Manager", "updateDialogTitle": "Opdater Manager",
"updateDialogText": "Er du sikker på, at du vil downloade og opdatere ReVanced Manager?", "updateChangelogTitle": "Ændringslog",
"notificationTitle": "Opdatering downloadet", "notificationTitle": "Opdatering downloadet",
"notificationText": "Tryk for at installere opdateringen", "notificationText": "Tryk for at installere opdateringen",
"downloadingMessage": "Downloader opdatering...", "downloadingMessage": "Downloader opdatering...",
@ -81,13 +82,13 @@
"all": "Alle", "all": "Alle",
"none": "Ingen", "none": "Ingen",
"loadPatchesSelection": "Indlæs patches udvælgelse", "loadPatchesSelection": "Indlæs patches udvælgelse",
"noSavedPatches": "Ingen gemte patches til den valgte app\nTryk på Udført for at gemme det aktuelle valg", "noSavedPatches": "Ingen gemte patches til den valgte app.\nTryk på Udført for at gemme det aktuelle valg.",
"noPatchesFound": "Ingen patches fundet til den valgte app", "noPatchesFound": "Ingen patches fundet til den valgte app",
"selectAllPatchesWarningContent": "Du er ved at vælge alle patches, der inkluderer ikke-anbefalede patches, og kan forårsage uønsket adfærd." "selectAllPatchesWarningContent": "Du er ved at vælge alle patches, der inkluderer ikke-anbefalede patches, og kan forårsage uønsket adfærd."
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "Valg af denne patch kan medføre patching-fejl.\n\nApp version: {packageVersion}\nUnderstøttede versioner:\n{supportedVersions}", "unsupportedDialogText": "Valg af denne patch kan medføre patching-fejl.\n\nApp version: {packageVersion}\nUnderstøttede versioner:\n{supportedVersions}",
"unsupportedPatchVersion": "Patch er ikke understøttet for denne app-version. Aktivér eksperimentel indstilling i indstillinger for at fortsætte." "unsupportedPatchVersion": "Patch er ikke understøttet for denne app-version. Aktivér den eksperimentelle indstilling i indstillinger for at fortsætte."
}, },
"installerView": { "installerView": {
"widgetTitle": "Installationsprogram", "widgetTitle": "Installationsprogram",
@ -121,6 +122,7 @@
"englishOption": "Engelsk", "englishOption": "Engelsk",
"sourcesLabel": "Kilder", "sourcesLabel": "Kilder",
"sourcesLabelHint": "Konfigurér dine brugerdefinerede kilder", "sourcesLabelHint": "Konfigurér dine brugerdefinerede kilder",
"hostRepositoryLabel": "Repository API",
"orgPatchesLabel": "Organisation for Patches", "orgPatchesLabel": "Organisation for Patches",
"sourcesPatchesLabel": "Kilde til Patches", "sourcesPatchesLabel": "Kilde til Patches",
"orgIntegrationsLabel": "Organisation for Integrationer", "orgIntegrationsLabel": "Organisation for Integrationer",
@ -135,6 +137,8 @@
"apiURLLabel": "API URL", "apiURLLabel": "API URL",
"apiURLHint": "Konfigurer din brugerdefineret API URL", "apiURLHint": "Konfigurer din brugerdefineret API URL",
"selectApiURL": "API URL", "selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "Understøttelse af eksperimentelle universale patches",
"experimentalUniversalPatchesHint": "Vis alle applikationer der skal bruges med universelle patches, indlæsningslisten over apps kan være langsommere",
"experimentalPatchesLabel": "Understøttelse af eksperimentelle patches", "experimentalPatchesLabel": "Understøttelse af eksperimentelle patches",
"experimentalPatchesHint": "Aktiver brugen af ikke-understøttede patches i enhver app version", "experimentalPatchesHint": "Aktiver brugen af ikke-understøttede patches i enhver app version",
"enabledExperimentalPatches": "Understøttelse af eksperimentelle patches aktiveret", "enabledExperimentalPatches": "Understøttelse af eksperimentelle patches aktiveret",
@ -148,7 +152,7 @@
"deleteKeystoreHint": "Slet keystore der bruges til at signere appen", "deleteKeystoreHint": "Slet keystore der bruges til at signere appen",
"deletedKeystore": "Keystore slettet", "deletedKeystore": "Keystore slettet",
"deleteTempDirLabel": "Slet midlertidige filer", "deleteTempDirLabel": "Slet midlertidige filer",
"deleteTempDirHint": "Slet de ubrugte midlertidige filer", "deleteTempDirHint": "Slet ubrugte midlertidige filer",
"deletedTempDir": "Midlertidige filer slettet", "deletedTempDir": "Midlertidige filer slettet",
"exportPatchesLabel": "Eksportér valgte patches", "exportPatchesLabel": "Eksportér valgte patches",
"exportPatchesHint": "Eksportér valgte patches til en JSON-fil", "exportPatchesHint": "Eksportér valgte patches til en JSON-fil",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "OK", "okButton": "OK",
"cancelButton": "Abbrechen", "cancelButton": "Abbrechen",
"updateButton": "Aktualisieren",
"enabledLabel": "Aktiviert", "enabledLabel": "Aktiviert",
"disabledLabel": "Deaktiviert", "disabledLabel": "Deaktiviert",
"yesButton": "Ja", "yesButton": "Ja",
@ -21,11 +22,11 @@
"noInstallations": "Keine gepatchten Apps installiert", "noInstallations": "Keine gepatchten Apps installiert",
"installed": "Installiert", "installed": "Installiert",
"updateDialogTitle": "Manager aktualisieren", "updateDialogTitle": "Manager aktualisieren",
"updateDialogText": "Bist du sicher, dass du den ReVanced Manager herunterladen und aktualisieren möchtest?", "updateChangelogTitle": "Änderungsverlauf",
"notificationTitle": "Update heruntergeladen", "notificationTitle": "Update heruntergeladen",
"notificationText": "Tippen um Update zu installieren", "notificationText": "Tippen um Update zu installieren",
"downloadingMessage": "Aktualisierung wird heruntergeladen...", "downloadingMessage": "Aktualisierung wird heruntergeladen...",
"installingMessage": "Aktualisierung wird installiert...", "installingMessage": "Update wird installiert...",
"errorDownloadMessage": "Aktualisierung konnte nicht heruntergeladen werden", "errorDownloadMessage": "Aktualisierung konnte nicht heruntergeladen werden",
"errorInstallMessage": "Aktualisierung konnte nicht installiert werden", "errorInstallMessage": "Aktualisierung konnte nicht installiert werden",
"noConnection": "Keine Internetverbindung", "noConnection": "Keine Internetverbindung",
@ -81,13 +82,13 @@
"all": "Alle", "all": "Alle",
"none": "Keine", "none": "Keine",
"loadPatchesSelection": "Patchauswahl laden", "loadPatchesSelection": "Patchauswahl laden",
"noSavedPatches": "Keine gespeicherten Patches für die ausgewählte App\nDrücken Sie Fertig, um die aktuelle Auswahl zu speichern", "noSavedPatches": "Keine gespeicherten Patches für die ausgewählte App. Drücke „Fertig” zum Speichern der aktuellen Auswahl.",
"noPatchesFound": "Keine Patches für die ausgewählte App gefunden", "noPatchesFound": "Keine Patches für die ausgewählte App gefunden",
"selectAllPatchesWarningContent": "Du bist dabei alle Patches auszuwählen, dies beinhaltet nicht empfohlene Patches und kann zu unerwünschtem Verhalten führen." "selectAllPatchesWarningContent": "Du bist dabei alle Patches auszuwählen, dies beinhaltet nicht empfohlene Patches und kann zu unerwünschtem Verhalten führen."
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "Die Auswahl dieses Patches kann zu Fehlern beim Patchen führen.\n\nApp-Version: {packageVersion}\nUnterstützte Versionen:\n{supportedVersions}", "unsupportedDialogText": "Die Auswahl dieses Patches kann zu Fehlern beim Patchen führen.\n\nApp-Version: {packageVersion}\nUnterstützte Versionen:\n{supportedVersions}",
"unsupportedPatchVersion": "Patch wird für diese App-Version nicht unterstützt. Aktiviere das experimentelle Feature in den Einstellungen, um fortzufahren." "unsupportedPatchVersion": "Der Patch unterstützt diese Version der App nicht. Aktiviere in den Einstellungen die experimentellen Funktionen, um fortzufahren."
}, },
"installerView": { "installerView": {
"widgetTitle": "Installer", "widgetTitle": "Installer",
@ -121,6 +122,7 @@
"englishOption": "Englisch", "englishOption": "Englisch",
"sourcesLabel": "Quellen", "sourcesLabel": "Quellen",
"sourcesLabelHint": "Konfiguriere deine eigenen Quellen", "sourcesLabelHint": "Konfiguriere deine eigenen Quellen",
"hostRepositoryLabel": "Repository-API",
"orgPatchesLabel": "Patches Organisation", "orgPatchesLabel": "Patches Organisation",
"sourcesPatchesLabel": "Quelle für Patche", "sourcesPatchesLabel": "Quelle für Patche",
"orgIntegrationsLabel": "Integrationen Organisation", "orgIntegrationsLabel": "Integrationen Organisation",
@ -135,6 +137,8 @@
"apiURLLabel": "API-URL", "apiURLLabel": "API-URL",
"apiURLHint": "Konfiguriere deine eigene API-URL", "apiURLHint": "Konfiguriere deine eigene API-URL",
"selectApiURL": "API URL", "selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "Unterstützung für universelle Patches (experimentell)",
"experimentalUniversalPatchesHint": "Zeigt alle Apps zur Verwendung mit universellen Patches, dadurch lädt die App-Liste evtl. langsamer",
"experimentalPatchesLabel": "Experimentelle Patches aktiviert", "experimentalPatchesLabel": "Experimentelle Patches aktiviert",
"experimentalPatchesHint": "Erlaubt die Benutzung von nicht unterstützten Patches mit jeder App Version", "experimentalPatchesHint": "Erlaubt die Benutzung von nicht unterstützten Patches mit jeder App Version",
"enabledExperimentalPatches": "Experimentelle Patches Unterstützung aktiviert", "enabledExperimentalPatches": "Experimentelle Patches Unterstützung aktiviert",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "Εντάξει", "okButton": "Εντάξει",
"cancelButton": "Ακύρωση", "cancelButton": "Ακύρωση",
"updateButton": "Ενημέρωση",
"enabledLabel": "Ενεργό", "enabledLabel": "Ενεργό",
"disabledLabel": "Ανενεργό", "disabledLabel": "Ανενεργό",
"yesButton": "Ναι", "yesButton": "Ναι",
@ -21,7 +22,7 @@
"noInstallations": "Δεν έχουν εγκατασταθεί τροποποιημένες εφαρμογές", "noInstallations": "Δεν έχουν εγκατασταθεί τροποποιημένες εφαρμογές",
"installed": "Εγκατεστημένες", "installed": "Εγκατεστημένες",
"updateDialogTitle": "Ενημέρωση του Manager", "updateDialogTitle": "Ενημέρωση του Manager",
"updateDialogText": "Είστε βέβαιοι ότι θέλετε να κατεβάσετε και να ενημερώσετε το ReVanced Manager;", "updateChangelogTitle": "Τι νέο υπάρχει",
"notificationTitle": "Η λήψη της ενημέρωσης ολοκληρώθηκε", "notificationTitle": "Η λήψη της ενημέρωσης ολοκληρώθηκε",
"notificationText": "Πατήστε για την εγκατάσταση της ενημέρωσης", "notificationText": "Πατήστε για την εγκατάσταση της ενημέρωσης",
"downloadingMessage": "Λήψη ενημέρωσης...", "downloadingMessage": "Λήψη ενημέρωσης...",
@ -81,7 +82,7 @@
"all": "Όλα", "all": "Όλα",
"none": "Κανένα", "none": "Κανένα",
"loadPatchesSelection": "Φόρτωση επιλεγμένων τροποποιήσεων", "loadPatchesSelection": "Φόρτωση επιλεγμένων τροποποιήσεων",
"noSavedPatches": "Δεν υπάρχουν αποθηκευμένες τροποποιήσεις για την εφαρμογή που επιλέξατε.\nΠατήστε «Τέλος» για να αποθηκεύσετε τις τωρινές επιλογές σας", "noSavedPatches": "Δεν υπάρχουν αποθηκευμένες τροποποιήσεις για την εφαρμογή που επιλέξατε.\nΠατήστε «Τέλος» για να αποθηκεύσετε τις τωρινές επιλογές σας.",
"noPatchesFound": "Δε βρέθηκαν τροποποιήσεις για την επιλεγμένη εφαρμογή", "noPatchesFound": "Δε βρέθηκαν τροποποιήσεις για την επιλεγμένη εφαρμογή",
"selectAllPatchesWarningContent": "Πρόκειται να επιλέξτε όλες τις μη συνιστώμενες τροποποιήσεις, μπορεί να προκληθεί ανεπιθύμητη συμπεριφορά." "selectAllPatchesWarningContent": "Πρόκειται να επιλέξτε όλες τις μη συνιστώμενες τροποποιήσεις, μπορεί να προκληθεί ανεπιθύμητη συμπεριφορά."
}, },
@ -121,6 +122,7 @@
"englishOption": "Αγγλικά", "englishOption": "Αγγλικά",
"sourcesLabel": "Πηγές", "sourcesLabel": "Πηγές",
"sourcesLabelHint": "Διαμορφώστε τις δικές σας προσαρμοσμένες πηγές", "sourcesLabelHint": "Διαμορφώστε τις δικές σας προσαρμοσμένες πηγές",
"hostRepositoryLabel": "Αποθετήριο API",
"orgPatchesLabel": "Οργάνωση τροποποιήσεων", "orgPatchesLabel": "Οργάνωση τροποποιήσεων",
"sourcesPatchesLabel": "Πηγή τροποποιήσεων", "sourcesPatchesLabel": "Πηγή τροποποιήσεων",
"orgIntegrationsLabel": "Οργάνωση ενσωματώσεων", "orgIntegrationsLabel": "Οργάνωση ενσωματώσεων",
@ -135,6 +137,8 @@
"apiURLLabel": "API URL", "apiURLLabel": "API URL",
"apiURLHint": "Διαμορφώστε το δικό σας προσαρμοσμένο API URL", "apiURLHint": "Διαμορφώστε το δικό σας προσαρμοσμένο API URL",
"selectApiURL": "Διεύθυνση URL API", "selectApiURL": "Διεύθυνση URL API",
"experimentalUniversalPatchesLabel": "Υποστήριξη γενικευμένων πειραματικών τροποποιήσεων",
"experimentalUniversalPatchesHint": "Εμφάνιση όλων των εφαρμογών (για χρήση τροποποιήσεων που λειτουργούν σε κάθε εφαρμογή), η φόρτωση της λίστας εφαρμογών μπορεί να καθυστερεί περισσότερο",
"experimentalPatchesLabel": "Πειραματική υποστήριξη τροποποιήσεων", "experimentalPatchesLabel": "Πειραματική υποστήριξη τροποποιήσεων",
"experimentalPatchesHint": "Ενεργοποιήστε το για χρήση μη υποστηριζόμενων τροποποιήσεων, ανεξαρτήτως έκδοσης εφαρμογής", "experimentalPatchesHint": "Ενεργοποιήστε το για χρήση μη υποστηριζόμενων τροποποιήσεων, ανεξαρτήτως έκδοσης εφαρμογής",
"enabledExperimentalPatches": "Πειραματική υποστήριξη τροποποιήσεων ενεργή", "enabledExperimentalPatches": "Πειραματική υποστήριξη τροποποιήσεων ενεργή",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "OK", "okButton": "OK",
"cancelButton": "Cancelar", "cancelButton": "Cancelar",
"updateButton": "Actualizar",
"enabledLabel": "Activado", "enabledLabel": "Activado",
"disabledLabel": "Desactivado", "disabledLabel": "Desactivado",
"yesButton": "Sí", "yesButton": "Sí",
@ -21,7 +22,7 @@
"noInstallations": "No hay aplicaciones parcheadas instaladas", "noInstallations": "No hay aplicaciones parcheadas instaladas",
"installed": "Instalado", "installed": "Instalado",
"updateDialogTitle": "Actualizar Manager", "updateDialogTitle": "Actualizar Manager",
"updateDialogText": "¿Está seguro de que desea descargar y actualizar ReVanced Manager?", "updateChangelogTitle": "Registro de cambios",
"notificationTitle": "Actualización descargada", "notificationTitle": "Actualización descargada",
"notificationText": "Toca para instalar la actualización", "notificationText": "Toca para instalar la actualización",
"downloadingMessage": "Descargando actualización...", "downloadingMessage": "Descargando actualización...",
@ -81,7 +82,7 @@
"all": "Todos", "all": "Todos",
"none": "Ninguno", "none": "Ninguno",
"loadPatchesSelection": "Cargar selección de parches", "loadPatchesSelection": "Cargar selección de parches",
"noSavedPatches": "No hay parches guardados para la app seleccionada\nPresione Hecho para guardar la selección actual", "noSavedPatches": "No hay parches guardados para la app seleccionada\nPresione Hecho para guardar la selección actual.",
"noPatchesFound": "No se encontraron parches para la aplicación seleccionada", "noPatchesFound": "No se encontraron parches para la aplicación seleccionada",
"selectAllPatchesWarningContent": "Está a punto de seleccionar todos los parches incluyendo parches no recomendados, lo cual puede causar comportamientos inesperados." "selectAllPatchesWarningContent": "Está a punto de seleccionar todos los parches incluyendo parches no recomendados, lo cual puede causar comportamientos inesperados."
}, },
@ -121,6 +122,7 @@
"englishOption": "Inglés", "englishOption": "Inglés",
"sourcesLabel": "Fuentes", "sourcesLabel": "Fuentes",
"sourcesLabelHint": "Configure sus fuentes personalizadas", "sourcesLabelHint": "Configure sus fuentes personalizadas",
"hostRepositoryLabel": "Repositorio API",
"orgPatchesLabel": "Organización de los parches", "orgPatchesLabel": "Organización de los parches",
"sourcesPatchesLabel": "Fuente de los parches", "sourcesPatchesLabel": "Fuente de los parches",
"orgIntegrationsLabel": "Organización de integraciones", "orgIntegrationsLabel": "Organización de integraciones",
@ -135,6 +137,8 @@
"apiURLLabel": "URL de la API", "apiURLLabel": "URL de la API",
"apiURLHint": "Configure su URL personalizada para la API", "apiURLHint": "Configure su URL personalizada para la API",
"selectApiURL": "URL de la API", "selectApiURL": "URL de la API",
"experimentalUniversalPatchesLabel": "Soporte experimental para parches universales",
"experimentalUniversalPatchesHint": "Mostrar todas las aplicaciones a usar con parches universales, la carga de la lista de aplicaciones puede ser más lenta",
"experimentalPatchesLabel": "Soporte para parches experimentales", "experimentalPatchesLabel": "Soporte para parches experimentales",
"experimentalPatchesHint": "Habilite el uso de parches no compatibles en cualquier versión de la app", "experimentalPatchesHint": "Habilite el uso de parches no compatibles en cualquier versión de la app",
"enabledExperimentalPatches": "Soporte para parches experimentales activado", "enabledExperimentalPatches": "Soporte para parches experimentales activado",
@ -148,7 +152,7 @@
"deleteKeystoreHint": "Eliminar el keystore usado para firmar la app", "deleteKeystoreHint": "Eliminar el keystore usado para firmar la app",
"deletedKeystore": "Keystore eliminado", "deletedKeystore": "Keystore eliminado",
"deleteTempDirLabel": "Borrar archivos temporales", "deleteTempDirLabel": "Borrar archivos temporales",
"deleteTempDirHint": "Elimina los archivos temporales no utilizados", "deleteTempDirHint": "Eliminar archivos temporales no utilizados",
"deletedTempDir": "Archivos temporales eliminados", "deletedTempDir": "Archivos temporales eliminados",
"exportPatchesLabel": "Exportar la selección de parches", "exportPatchesLabel": "Exportar la selección de parches",
"exportPatchesHint": "Exportar selección de parches a un archivo JSON", "exportPatchesHint": "Exportar selección de parches a un archivo JSON",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "OK", "okButton": "OK",
"cancelButton": "Annuler", "cancelButton": "Annuler",
"updateButton": "Mise à jour",
"enabledLabel": "Activé", "enabledLabel": "Activé",
"disabledLabel": "Désactivé", "disabledLabel": "Désactivé",
"yesButton": "Oui", "yesButton": "Oui",
@ -21,9 +22,9 @@
"noInstallations": "Aucune application patchée n'est installée", "noInstallations": "Aucune application patchée n'est installée",
"installed": "Installée", "installed": "Installée",
"updateDialogTitle": "Mettre à jour le Manager", "updateDialogTitle": "Mettre à jour le Manager",
"updateDialogText": "Êtes-vous sûr de vouloir télécharger et mettre à jour ReVanced Manager?", "updateChangelogTitle": "Liste des changements",
"notificationTitle": "Mise à jour téléchargée", "notificationTitle": "Mise à jour téléchargée",
"notificationText": "Appuyer pour installer la mise à jour", "notificationText": "Appuyez sur pour installer la mise à jour",
"downloadingMessage": "Téléchargement de la mise à jour...", "downloadingMessage": "Téléchargement de la mise à jour...",
"installingMessage": "Installation de la mise à jour...", "installingMessage": "Installation de la mise à jour...",
"errorDownloadMessage": "Impossible de télécharger la mise à jour", "errorDownloadMessage": "Impossible de télécharger la mise à jour",
@ -51,7 +52,7 @@
"appSelectorCard": { "appSelectorCard": {
"widgetTitle": "Sélectionner une application", "widgetTitle": "Sélectionner une application",
"widgetTitleSelected": "Application sélectionnée", "widgetTitleSelected": "Application sélectionnée",
"widgetSubtitle": "Aucune application n'a été 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",
@ -80,14 +81,14 @@
"recommended": "Recommandé", "recommended": "Recommandé",
"all": "Tout", "all": "Tout",
"none": "Aucun", "none": "Aucun",
"loadPatchesSelection": "Charger les patches sélectionnés", "loadPatchesSelection": "Charger les patchs sélectionnés",
"noSavedPatches": "Aucun patch enregistré pour l'application sélectionnée\nAppuyez sur Terminé pour enregistrer la sélection actuelle", "noSavedPatches": "Aucun patch enregistré pour l'application sélectionnée.\nAppuyez sur Terminé pour enregistrer la sélection actuelle.",
"noPatchesFound": "Aucun patch n'a été trouvé pour l'application sélectionnée", "noPatchesFound": "Aucun patch n'a été trouvé pour l'application sélectionnée",
"selectAllPatchesWarningContent": "Vous êtes sur le point de sélectionner tous les patchs, cela inclut des patchs non recommandés et peut causer des comportements indésirables." "selectAllPatchesWarningContent": "Vous êtes sur le point de sélectionner tous les patchs, cela inclut des patchs non recommandés et peut causer des comportements indésirables."
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "Sélectionner ce patch pourrait entrainer des erreurs dans la modification.\n\nVersion de l'application: {packageVersion}\nVersions supportées:\n{supportedVersions}", "unsupportedDialogText": "La sélection de ce patch peut entraîner des erreurs.\n\nVersion de l'application : {packageVersion}\nVersions prises en charge :\n{supportedVersions}",
"unsupportedPatchVersion": "Le patch n'est pas supporté pour cette version de l'application. Activez le commutateur expérimental dans les réglages pour continuer." "unsupportedPatchVersion": "Le correctif n'est pas pris en charge pour cette version de l'application. Activez le bouton expérimental dans les paramètres pour continuer."
}, },
"installerView": { "installerView": {
"widgetTitle": "Installateur", "widgetTitle": "Installateur",
@ -99,7 +100,7 @@
"notificationText": "Appuyer pour revenir à linstallateur", "notificationText": "Appuyer pour revenir à linstallateur",
"shareApkMenuOption": "Partager le fichier APK", "shareApkMenuOption": "Partager le fichier APK",
"exportApkMenuOption": "Exporter l'APK", "exportApkMenuOption": "Exporter l'APK",
"shareLogMenuOption": "Partager le log", "shareLogMenuOption": "Partager le journal",
"installErrorDialogTitle": "Erreur", "installErrorDialogTitle": "Erreur",
"installErrorDialogText1": "L'installation Root n'est pas possible avec la sélection actuelle de patchs.\nRe-patchez votre application ou choisissez une installation non root.", "installErrorDialogText1": "L'installation Root n'est pas possible avec la sélection actuelle de patchs.\nRe-patchez votre application ou choisissez une installation non root.",
"installErrorDialogText2": "L'installation Non-root n'est pas possible avec la sélection actuelle de patchs.\nRe-patchez votre application ou choisissez une installation en tant que Root.", "installErrorDialogText2": "L'installation Non-root n'est pas possible avec la sélection actuelle de patchs.\nRe-patchez votre application ou choisissez une installation en tant que Root.",
@ -121,6 +122,7 @@
"englishOption": "Anglais", "englishOption": "Anglais",
"sourcesLabel": "Sources", "sourcesLabel": "Sources",
"sourcesLabelHint": "Configurer vos sources personnalisées", "sourcesLabelHint": "Configurer vos sources personnalisées",
"hostRepositoryLabel": "Dépôt de l'API",
"orgPatchesLabel": "Organisation des patchs", "orgPatchesLabel": "Organisation des patchs",
"sourcesPatchesLabel": "Source des patches", "sourcesPatchesLabel": "Source des patches",
"orgIntegrationsLabel": "Organisme d'intégration", "orgIntegrationsLabel": "Organisme d'intégration",
@ -135,6 +137,8 @@
"apiURLLabel": "URL de l'API", "apiURLLabel": "URL de l'API",
"apiURLHint": "Configurez l'URL de votre API personnalisée", "apiURLHint": "Configurez l'URL de votre API personnalisée",
"selectApiURL": "URL de l'API", "selectApiURL": "URL de l'API",
"experimentalUniversalPatchesLabel": "Support expérimental des patchs universels",
"experimentalUniversalPatchesHint": "Afficher toutes les applications à utiliser avec les patchs universels, le chargement de la liste des applications pourrait être lent",
"experimentalPatchesLabel": "Support des patchs expérimentaux", "experimentalPatchesLabel": "Support des patchs expérimentaux",
"experimentalPatchesHint": "Activer l'utilisation des patchs non supportés dans n'importe quelle version de l'application", "experimentalPatchesHint": "Activer l'utilisation des patchs non supportés dans n'importe quelle version de l'application",
"enabledExperimentalPatches": "Support pour les patchs expérimentaux activé", "enabledExperimentalPatches": "Support pour les patchs expérimentaux activé",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "Oke", "okButton": "Oke",
"cancelButton": "Batal", "cancelButton": "Batal",
"updateButton": "Perbarui",
"enabledLabel": "Aktif", "enabledLabel": "Aktif",
"disabledLabel": "Nonaktif", "disabledLabel": "Nonaktif",
"yesButton": "Ya", "yesButton": "Ya",
@ -21,7 +22,7 @@
"noInstallations": "Tidak ada aplikasi termodifikasi yang terpasang", "noInstallations": "Tidak ada aplikasi termodifikasi yang terpasang",
"installed": "Terpasang", "installed": "Terpasang",
"updateDialogTitle": "Perbarui Manager", "updateDialogTitle": "Perbarui Manager",
"updateDialogText": "Apakah Anda yakin ingin mengunduh dan memperbarui ReVanced Manager?", "updateChangelogTitle": "Catatan perubahan",
"notificationTitle": "Pembaruan sudah diunduh", "notificationTitle": "Pembaruan sudah diunduh",
"notificationText": "Ketuk untuk memasang pembaharuan", "notificationText": "Ketuk untuk memasang pembaharuan",
"downloadingMessage": "Mengunduh pembaruan...", "downloadingMessage": "Mengunduh pembaruan...",
@ -81,13 +82,13 @@
"all": "Semua", "all": "Semua",
"none": "Tidak ada", "none": "Tidak ada",
"loadPatchesSelection": "Tidak ada modifikasi yang terpilih", "loadPatchesSelection": "Tidak ada modifikasi yang terpilih",
"noSavedPatches": "Tidak ada modifikasi tersimpan untuk aplikasi terpilih", "noSavedPatches": "Tidak ada tambalan tersimpan untuk aplikasi terpilih.\nTekan \"Sudah\" untuk menyimpan seleksi saat ini.",
"noPatchesFound": "Modifikasi tidak ditemukan untuk aplikasi terpilih", "noPatchesFound": "Modifikasi tidak ditemukan untuk aplikasi terpilih",
"selectAllPatchesWarningContent": "Anda akan memilih semua modifikasi, termasuk modifikasi yang tidak direkomendasikan dan dapat mengakibatkan hal yang tidak diinginkan." "selectAllPatchesWarningContent": "Anda akan memilih semua modifikasi, termasuk modifikasi yang tidak direkomendasikan dan dapat mengakibatkan hal yang tidak diinginkan."
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "Menggunakan modifikasi ini dapat menyebabkan error pada saat memodifikasi aplikasi\n\nVersi aplikasi: {packageVersion}\nVersi yang mendukung:\n{supportedVersions}", "unsupportedDialogText": "Menggunakan modifikasi ini dapat menyebabkan error pada saat memodifikasi aplikasi\n\nVersi aplikasi: {packageVersion}\nVersi yang mendukung:\n{supportedVersions}",
"unsupportedPatchVersion": "Modifikasi tidak didukung untuk versi aplikasi ini. Aktifkan eksperimental dalam pengaturan untuk melanjutkan." "unsupportedPatchVersion": "Patch tidak didukung untuk versi aplikasi ini. Aktifkan tombol eksperimental di pengaturan untuk melanjutkan."
}, },
"installerView": { "installerView": {
"widgetTitle": "Pemasang", "widgetTitle": "Pemasang",
@ -121,6 +122,7 @@
"englishOption": "Inggris", "englishOption": "Inggris",
"sourcesLabel": "Sumber", "sourcesLabel": "Sumber",
"sourcesLabelHint": "Konfigurasi sumber kustom Anda", "sourcesLabelHint": "Konfigurasi sumber kustom Anda",
"hostRepositoryLabel": "API Repositori",
"orgPatchesLabel": "Organisasi Modifikasi", "orgPatchesLabel": "Organisasi Modifikasi",
"sourcesPatchesLabel": "Sumber Modifikasi", "sourcesPatchesLabel": "Sumber Modifikasi",
"orgIntegrationsLabel": "Organisasi Intergrasi", "orgIntegrationsLabel": "Organisasi Intergrasi",
@ -135,6 +137,8 @@
"apiURLLabel": "URL API", "apiURLLabel": "URL API",
"apiURLHint": "Konfigurasi URL API kustom Anda", "apiURLHint": "Konfigurasi URL API kustom Anda",
"selectApiURL": "URL API", "selectApiURL": "URL API",
"experimentalUniversalPatchesLabel": "Dukungan tambalan universal eksperimental",
"experimentalUniversalPatchesHint": "Menampilkan semua aplikasi yang digunakan tambalan universal, pemuatan daftar aplikasi akan membuat lambat",
"experimentalPatchesLabel": "Dukungan Modifikasi Eksperimental", "experimentalPatchesLabel": "Dukungan Modifikasi Eksperimental",
"experimentalPatchesHint": "Aktifkan untuk menggunakan modifikasi yang tidak didukung di versi aplikasi apa pun", "experimentalPatchesHint": "Aktifkan untuk menggunakan modifikasi yang tidak didukung di versi aplikasi apa pun",
"enabledExperimentalPatches": "Modifikasi eksperimental diaktifkan", "enabledExperimentalPatches": "Modifikasi eksperimental diaktifkan",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "OK", "okButton": "OK",
"cancelButton": "Annulla", "cancelButton": "Annulla",
"updateButton": "Aggiorna",
"enabledLabel": "Attivo", "enabledLabel": "Attivo",
"disabledLabel": "Disattivato", "disabledLabel": "Disattivato",
"yesButton": "Sì", "yesButton": "Sì",
@ -21,7 +22,7 @@
"noInstallations": "Non è stata installata nessuna applicazione modificata", "noInstallations": "Non è stata installata nessuna applicazione modificata",
"installed": "Installate", "installed": "Installate",
"updateDialogTitle": "Aggiorna Manager", "updateDialogTitle": "Aggiorna Manager",
"updateDialogText": "Sei sicuro di voler scaricare e aggiornare ReVanced Manager?", "updateChangelogTitle": "Changelog",
"notificationTitle": "Aggiornamento scaricato", "notificationTitle": "Aggiornamento scaricato",
"notificationText": "Tocca per installare l'aggiornamento", "notificationText": "Tocca per installare l'aggiornamento",
"downloadingMessage": "Sto scaricando l'aggiornamento...", "downloadingMessage": "Sto scaricando l'aggiornamento...",
@ -81,7 +82,7 @@
"all": "Tutte", "all": "Tutte",
"none": "Nessuna", "none": "Nessuna",
"loadPatchesSelection": "Carica selezione patch", "loadPatchesSelection": "Carica selezione patch",
"noSavedPatches": "Nessuna patch salvata per l'app selezionata\nPremi Fatto per salvare la selezione corrente", "noSavedPatches": "Nessuna patch salvata per l'app selezionata.\nPremi Fatto per salvare la selezione corrente.",
"noPatchesFound": "Nessuna patch trovata per l'applicazione selezionata", "noPatchesFound": "Nessuna patch trovata per l'applicazione selezionata",
"selectAllPatchesWarningContent": "Stai per selezionare tutte le patch, incluse patch non consigliate che potrebbero causare comportamenti indesiderati." "selectAllPatchesWarningContent": "Stai per selezionare tutte le patch, incluse patch non consigliate che potrebbero causare comportamenti indesiderati."
}, },
@ -121,6 +122,7 @@
"englishOption": "English", "englishOption": "English",
"sourcesLabel": "Sorgenti", "sourcesLabel": "Sorgenti",
"sourcesLabelHint": "Configura le tue sorgenti personalizzate", "sourcesLabelHint": "Configura le tue sorgenti personalizzate",
"hostRepositoryLabel": "Repository API",
"orgPatchesLabel": "Organizzazione Patch", "orgPatchesLabel": "Organizzazione Patch",
"sourcesPatchesLabel": "Sorgente Patch", "sourcesPatchesLabel": "Sorgente Patch",
"orgIntegrationsLabel": "Organizzazione Integrazioni", "orgIntegrationsLabel": "Organizzazione Integrazioni",
@ -135,6 +137,8 @@
"apiURLLabel": "URL API", "apiURLLabel": "URL API",
"apiURLHint": "Configura il tuo URL delle API personalizzato", "apiURLHint": "Configura il tuo URL delle API personalizzato",
"selectApiURL": "URL API", "selectApiURL": "URL API",
"experimentalUniversalPatchesLabel": "Supporto per patch universali sperimentali",
"experimentalUniversalPatchesHint": "Visualizza tutte le applicazioni da utilizzare con le patch universali, il caricamento dell'elenco delle app potrebbe essere più lento",
"experimentalPatchesLabel": "Supporto per patch sperimentali", "experimentalPatchesLabel": "Supporto per patch sperimentali",
"experimentalPatchesHint": "Abilita l'utilizzo di patch non supportate in qualsiasi versione dell'app", "experimentalPatchesHint": "Abilita l'utilizzo di patch non supportate in qualsiasi versione dell'app",
"enabledExperimentalPatches": "Supporto patch sperimentali abilitato", "enabledExperimentalPatches": "Supporto patch sperimentali abilitato",

200
assets/i18n/lt_LT.json Normal file
View File

@ -0,0 +1,200 @@
{
"okButton": "Gerai",
"cancelButton": "Atšaukti",
"updateButton": "Atnaujinti",
"enabledLabel": "Įjungta",
"disabledLabel": "Išjungta",
"yesButton": "Taip",
"noButton": "Ne",
"warning": "Įspėjimas",
"navigationView": {
"dashboardTab": "Valdymo skydas",
"patcherTab": "Patcher",
"settingsTab": "Nustatymai"
},
"homeView": {
"widgetTitle": "Valdymo skydas",
"updatesSubtitle": "Atnaujinimai",
"patchedSubtitle": "Modifikuotos programos",
"updatesAvailable": "Galimi atnaujinimai",
"noUpdates": "Atnaujinimų nėra",
"WIP": "Vykdomi darbai...",
"noInstallations": "Nėra įdiegtų modifikuotų programų",
"installed": "Įdiegta",
"updateDialogTitle": "Atnaujinti Manager",
"updateChangelogTitle": "Pakeitimų sąrašas",
"notificationTitle": "Atnaujinimas atsiųstas",
"notificationText": "Paspauskite, kad įdiegtumėte naujinimą",
"downloadingMessage": "Atsiunčiamas atnaujinimas...",
"installingMessage": "Įdiegiamas atnaujinimas...",
"errorDownloadMessage": "Nepavyksta atsisiųsti atnaujinimo",
"errorInstallMessage": "Nepavyksta įdiegti atnaujinimo",
"noConnection": "Nėra interneto ryšio",
"updatesDisabled": "Modifikuotų programų atnaujinimas šiuo metu išjungtas. Vėl modifikuokite programą."
},
"applicationItem": {
"patchButton": "Modifikuoti",
"infoButton": "Info",
"changelogLabel": "Pakeitimai"
},
"latestCommitCard": {
"loadingLabel": "Kraunama...",
"timeagoLabel": "Prieš {time}",
"patcherLabel": "Patcher: ",
"managerLabel": "Manager: ",
"updateButton": "Atnaujinti Manager"
},
"patcherView": {
"widgetTitle": "Patcher",
"patchButton": "Modifikuoti",
"patchDialogText": "Jūs pasirinkote resource modifikaciją ir padalinta APK instaliacija buvo aptikta, tai modifikavimo klaidos gali atsitikti.\nAr esate tikri kad norite tęsti modifikuoti padalytą APK?"
},
"appSelectorCard": {
"widgetTitle": "Pasirinkite programą",
"widgetTitleSelected": "Pasirinkta programa",
"widgetSubtitle": "Nepasirinkta programa",
"noAppsLabel": "Nerastos jokios programos",
"currentVersion": "Dabartinė",
"recommendedVersion": "Rekomenduojama",
"anyVersion": "bet kokia"
},
"patchSelectorCard": {
"widgetTitle": "Pasirinkite modifikacijas",
"widgetTitleSelected": "Pasirinktos modifikacijos",
"widgetSubtitle": "Pirmiausia pasirinkite programą",
"widgetEmptySubtitle": "Nepasirinkta jokių modifikacijų"
},
"socialMediaCard": {
"widgetTitle": "Socialiniai tinklai",
"widgetSubtitle": "Mes esame internete!"
},
"appSelectorView": {
"viewTitle": "Pasirinkite programą",
"searchBarHint": "Ieškoti programų",
"storageButton": "Saugykla",
"errorMessage": "Neina naudoti parinktos programos"
},
"patchesSelectorView": {
"viewTitle": "Pasirinkti modifikacijas",
"searchBarHint": "Ieškoti modifikacijų",
"doneButton": "Atlikta",
"recommended": "Rekomenduojama",
"all": "Visi",
"none": "Nėra",
"loadPatchesSelection": "Įkelti modifikacijų pasirinkimą",
"noSavedPatches": "Nėra išsaugotų pasirinktos programos modifikacijų.\nPaspauskite Atlikta, kad išsaugotumėte dabartinį pasirinkimą.",
"noPatchesFound": "Nerasta modifikacijų pasirinktai programai",
"selectAllPatchesWarningContent": "Jūs pasirinksite visas modifikacijas, tai įskaito ir nerekomenduojamas modifikacijas ir tai gali sukelti nepageidaujama elgesį."
},
"patchItem": {
"unsupportedDialogText": "Šios modifikacijos pasirinkimas gali sukelti modifikavimo klaidų.\n\nProgramos versija: {packageVersion}\npalaikomos versijos:\n{supportedVersions}",
"unsupportedPatchVersion": "Modifikacija nepalaikoma šios programos versijai. Norėdami tęsti, nustatymuose įjunkite eksperimentinį jungiklį."
},
"installerView": {
"widgetTitle": "Įdiegėjas",
"installButton": "Įdiegti",
"installRootButton": "Įdiegti kaip Root",
"openButton": "Atidaryti",
"shareButton": "Bendrinti failą",
"notificationTitle": "ReVanced Manager modifikuoja programą",
"notificationText": "Paspauskite sugrįžti į įdiegėją",
"shareApkMenuOption": "Bendrinti APK",
"exportApkMenuOption": "Eksportuoti APK",
"shareLogMenuOption": "Bendrinti įrašus",
"installErrorDialogTitle": "Klaida",
"installErrorDialogText1": "Root instaliacija negalima su dabartinėm pasirinktom modifikacijom.\nPer modifikuok programą arba pasirink ne root instaliaciją.",
"installErrorDialogText2": "Ne root instaliacija negalima su dabartinėm pasirinktom modifikacijom.\nPer modifikuok programą arba pasirink root instaliaciją jei tavo įrenginys turi root.",
"installErrorDialogText3": "Root instaliacija negalima nes originalus APK buvo pasirinktas iš saugyklos.\nPasirink jau instaliuotą programą arba pasirink ne root instaliaciją.",
"noExit": "Diegimo programa vis dar veikia, negalima išeiti..."
},
"settingsView": {
"widgetTitle": "Nustatymai",
"appearanceSectionTitle": "Išvaizda",
"teamSectionTitle": "Komanda",
"infoSectionTitle": "Info",
"advancedSectionTitle": "Išplėstiniai nustatymai",
"logsSectionTitle": "Įrašai",
"darkThemeLabel": "Tamsus rėžimas",
"darkThemeHint": "Sveikas atvykęs į tamsiąją pusę",
"dynamicThemeLabel": "Material You",
"dynamicThemeHint": "Mėgaukis patirtimi artimiau tavo įrenginiui",
"languageLabel": "Kalba",
"englishOption": "Anglų",
"sourcesLabel": "Šaltiniai",
"sourcesLabelHint": "Sukonfigūruoti tavo nurodytus šaltinius",
"hostRepositoryLabel": "API saugykla",
"orgPatchesLabel": "Modifikacijų organizacija",
"sourcesPatchesLabel": "Modifikacijų šaltinis",
"orgIntegrationsLabel": "Integracijų organizacija",
"sourcesIntegrationsLabel": "Integracijų šaltinis",
"sourcesResetDialogTitle": "Nustatyti iš naujo",
"sourcesResetDialogText": "Ar esi tikras kad nori iš naujo nustatyti šaltinius į jų numatytas reikšmes?",
"apiURLResetDialogText": "Ar esi tikras kad nori iš naujo nustatyti API URL adresą į numatytą reikšmę?",
"contributorsLabel": "Prisidėjusieji žmonės",
"contributorsHint": "Žmonės prisidėję prie ReVanced",
"logsLabel": "Įrašai",
"logsHint": "Bendrinti Manager įrašus",
"apiURLLabel": "API URL",
"apiURLHint": "Sukonfigūruoti tavo nurodytus API URL",
"selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "Eksperimentinių universalių modifikacijų palaikymas",
"experimentalUniversalPatchesHint": "Rodyti visas programas, skirtas naudoti su universaliom modifikacijom, krovimas programų sąrašo gali būti lėtesnis",
"experimentalPatchesLabel": "Eksperimentinių modifikacijų palaikymas",
"experimentalPatchesHint": "Įgalinti naudoti nepalaikomas modifikacijas bet kurioje programos versijoje",
"enabledExperimentalPatches": "Eksperimentinių modifikacijų palaikymas įjungtas",
"exportSectionTitle": "Importuoti ir eksportuoti",
"aboutLabel": "Apie",
"snackbarMessage": "Nukopijuota į iškarpinę",
"sentryLabel": "Klaidų rinkimo įrankio įrašai",
"sentryHint": "Siųsti anoniminius įrašus, kad padėtumėte mums tobulinti ReVanced Manager",
"restartAppForChanges": "Iš naujo paleiskite programą, kad modifikacijos įsigaliotų",
"deleteKeystoreLabel": "Ištrinti keystore",
"deleteKeystoreHint": "Ištrinti keystore kuris naudojamas pasirašyti programą",
"deletedKeystore": "Keystore ištrintas",
"deleteTempDirLabel": "Ištrinti laikinus failus",
"deleteTempDirHint": "Ištrinti nenaudojamus laikinus failus",
"deletedTempDir": "Laikini failai ištrinti",
"exportPatchesLabel": "Eksportuoti modifikacijų pasirinkimą",
"exportPatchesHint": "Eksportuoti modifikacijų pasirinkimą į JSON failą",
"exportedPatches": "Modifikacijų pasirinkimai eksportuoti",
"noExportFileFound": "Nėra pasirinktų modifikacijų eksportuoti",
"importPatchesLabel": "Įkelti modifikacijų pasirinkimą",
"importPatchesHint": "Įkelti modifikacijų pasirinkimą iš JSON failo",
"importedPatches": "Modifikacijų pasirinkimai įkelti",
"resetStoredPatchesLabel": "Atstatyti modifikacijas",
"resetStoredPatchesHint": "Atstatyti išsaugotą modifikacijų pasirinkimą",
"resetStoredPatches": "Modifikacijų pasirinkimas buvo atstatytas",
"jsonSelectorErrorMessage": "Neina naudoti pasirinkto JSON failo",
"deleteLogsLabel": "Ištrinti įrašus",
"deleteLogsHint": "Ištrinti surinktus Manager įrašus",
"deletedLogs": "Įrašai ištrinti"
},
"appInfoView": {
"widgetTitle": "Programos informacija",
"openButton": "Atidaryti",
"uninstallButton": "Išdiegti",
"patchButton": "Modifikuoti",
"unpatchButton": "Išimti modifikacijas",
"unpatchDialogText": "Ar esi tikras kad nori išimti modifikacijas iš šios programos?",
"rootDialogTitle": "Klaida",
"rootDialogText": "Programa buvo įdiegta su supervartotojo leidimais, bet ReVanced Manager neturi leidimų.\nPirmiausia suteikite supervartotojo leidimus.",
"packageNameLabel": "Paketo pavadinimas",
"originalPackageNameLabel": "Originalus paketo pavadinimas",
"installTypeLabel": "Įdiegimo tipas",
"rootTypeLabel": "Root",
"nonRootTypeLabel": "Ne root",
"patchedDateLabel": "Modifikavimo data",
"patchedDateHint": "{date} {time}",
"appliedPatchesLabel": "Uždėtos modifikacijos",
"appliedPatchesHint": "{quantity} uždėtos modifikacijos",
"updateNotImplemented": "Ši funkcija dar neįgyvendinta"
},
"contributorsView": {
"widgetTitle": "Prisidėjusieji žmonės",
"patcherContributors": "Prisidėjusieji prie Patcher",
"patchesContributors": "Prisidėjusieji prie Patcher",
"integrationsContributors": "Prisidėjusieji prie integracijų",
"cliContributors": "Prisidėjusieji prie CLI",
"managerContributors": "Prisidėjusieji prie Manager"
}
}

View File

@ -1,6 +1,7 @@
{ {
"okButton": "OK", "okButton": "OK",
"cancelButton": "Anuluj", "cancelButton": "Anuluj",
"updateButton": "Aktualizuj",
"enabledLabel": "Włączone", "enabledLabel": "Włączone",
"disabledLabel": "Wyłączone", "disabledLabel": "Wyłączone",
"yesButton": "Tak", "yesButton": "Tak",
@ -21,7 +22,7 @@
"noInstallations": "Nie zainstalowano żadnych zmodyfikowanych aplikacji", "noInstallations": "Nie zainstalowano żadnych zmodyfikowanych aplikacji",
"installed": "Zainstalowane", "installed": "Zainstalowane",
"updateDialogTitle": "Zaktualizuj Managera", "updateDialogTitle": "Zaktualizuj Managera",
"updateDialogText": "Czy na pewno chcesz pobrać i zaktualizować ReVanced Manager?", "updateChangelogTitle": "Lista zmian",
"notificationTitle": "Pobrano aktualizacje", "notificationTitle": "Pobrano aktualizacje",
"notificationText": "Kliknij, aby zainstalować aktualizację", "notificationText": "Kliknij, aby zainstalować aktualizację",
"downloadingMessage": "Pobieranie aktualizacji...", "downloadingMessage": "Pobieranie aktualizacji...",
@ -29,7 +30,7 @@
"errorDownloadMessage": "Nie udało się pobrać aktualizacji", "errorDownloadMessage": "Nie udało się pobrać aktualizacji",
"errorInstallMessage": "Nie udało się zainstalować aktualizacji", "errorInstallMessage": "Nie udało się zainstalować aktualizacji",
"noConnection": "Brak połączenia z internetem", "noConnection": "Brak połączenia z internetem",
"updatesDisabled": "Aktualizowanie nowej wersji aplikacji jest obecnie niemożliwe. Spróbuj zaktualizować aplikację jeszcze raz." "updatesDisabled": "Aktualizowanie załatanej aplikacji jest obecnie niemożliwe. Spróbuj załatać aplikację jeszcze raz."
}, },
"applicationItem": { "applicationItem": {
"patchButton": "Łataj", "patchButton": "Łataj",
@ -75,33 +76,33 @@
}, },
"patchesSelectorView": { "patchesSelectorView": {
"viewTitle": "Wybierz łatki", "viewTitle": "Wybierz łatki",
"searchBarHint": "Wyszukaj aktualizacje", "searchBarHint": "Wyszukaj łatki",
"doneButton": "Gotowe", "doneButton": "Gotowe",
"recommended": "Rekomendowane", "recommended": "Zalecane",
"all": "Wszystkie", "all": "Wszystkie",
"none": "Żadne", "none": "Żadne",
"loadPatchesSelection": "Wczytaj wybór aktualizacji", "loadPatchesSelection": "Załaduj wybrane łatki",
"noSavedPatches": "Brak zapisanych aktualizacji dla wybranej aplikacji\nNaciśnij Gotowe, aby zapisać bieżący wybór", "noSavedPatches": "Brak zapisanych łatek dla wybranej aplikacji.\nNaciśnij Gotowe, aby zapisać bieżący wybór.",
"noPatchesFound": "Nie znaleziono żadnych łatek dla wybranej aplikacji", "noPatchesFound": "Nie znaleziono żadnych łatek dla wybranej aplikacji",
"selectAllPatchesWarningContent": "Zamierzasz wybrać wszystkie łatki, włącznie z tymi niezalecanymi, które mogą powodować niepożądane zachowania." "selectAllPatchesWarningContent": "Zamierzasz wybrać wszystkie łatki, włącznie z tymi niezalecanymi, które mogą powodować niepożądane zachowania."
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "Wybranie tej łatki może spowodować błędy podczas modyfikowania.\n\nWersja aplikacji: {packageVersion}\nAktualnie wspierana wersja:\n{supportedVersions}", "unsupportedDialogText": "Wybranie tej łatki może spowodować błędy podczas modyfikowania.\n\nWersja aplikacji: {packageVersion}\nAktualnie wspierana wersja:\n{supportedVersions}",
"unsupportedPatchVersion": "Łatka nie jest obsługiwana dla tej wersji aplikacji. Włącz eksperymentalne przełączanie w ustawieniach, aby kontynuować." "unsupportedPatchVersion": "Łatka nie jest obsługiwana dla tej wersji aplikacji. Włącz opcje eksperymentalne w ustawieniach, aby kontynuować."
}, },
"installerView": { "installerView": {
"widgetTitle": "Instalator", "widgetTitle": "Instalator",
"installButton": "Zainstaluj", "installButton": "Zainstaluj",
"installRootButton": "Instaluj jako Root", "installRootButton": "Zainstaluj jako Root",
"openButton": "Otwórz", "openButton": "Otwórz",
"shareButton": "Udostępnij plik", "shareButton": "Udostępnij plik",
"notificationTitle": "ReVanced Manager jest aktualizowany", "notificationTitle": "ReVanced Manager patchuje",
"notificationText": "Dotknij, aby powrócić do instalatora", "notificationText": "Dotknij, aby powrócić do instalatora",
"shareApkMenuOption": "Udostępnij plik APK", "shareApkMenuOption": "Udostępnij plik APK",
"exportApkMenuOption": "Eksportuj APK", "exportApkMenuOption": "Eksportuj APK",
"shareLogMenuOption": "Udostępnij logi", "shareLogMenuOption": "Udostępnij logi",
"installErrorDialogTitle": "Błąd", "installErrorDialogTitle": "Błąd",
"installErrorDialogText1": "Instalacja za pomocą roota nie jest możliwa przy obecnym wyborze aktualizacji.\nZaktualizuj aplikacje ponownie lub wybierz instalacje bez roota.", "installErrorDialogText1": "Instalacja za pomocą roota nie jest możliwa przy obecnym wyborze łatek.\nZałataj aplikacje ponownie lub wybierz instalacje bez roota.",
"installErrorDialogText2": "Instalacja bez roota nie jest możliwa przy obecnym wyborze łatek.\nZałataj aplikacje ponownie lub wybierz instalacje za pomocą roota, jeśli masz zrootowane urządzenie.", "installErrorDialogText2": "Instalacja bez roota nie jest możliwa przy obecnym wyborze łatek.\nZałataj aplikacje ponownie lub wybierz instalacje za pomocą roota, jeśli masz zrootowane urządzenie.",
"installErrorDialogText3": "Instalacja jako root nie jest możliwa, ponieważ oryginalny plik APK został wybrany z pamięci.\nWybierz zainstalowaną aplikację lub wybierz instalację bez używania roota.", "installErrorDialogText3": "Instalacja jako root nie jest możliwa, ponieważ oryginalny plik APK został wybrany z pamięci.\nWybierz zainstalowaną aplikację lub wybierz instalację bez używania roota.",
"noExit": "Instalator jest nadal uruchomiony, nie można zakończyć jego działania..." "noExit": "Instalator jest nadal uruchomiony, nie można zakończyć jego działania..."
@ -121,6 +122,7 @@
"englishOption": "Angielski", "englishOption": "Angielski",
"sourcesLabel": "Źródła", "sourcesLabel": "Źródła",
"sourcesLabelHint": "Skonfiguruj własne źródła", "sourcesLabelHint": "Skonfiguruj własne źródła",
"hostRepositoryLabel": "Repozytorium API",
"orgPatchesLabel": "Organizacja łatek", "orgPatchesLabel": "Organizacja łatek",
"sourcesPatchesLabel": "Źródło łatek", "sourcesPatchesLabel": "Źródło łatek",
"orgIntegrationsLabel": "Organizacja integracji", "orgIntegrationsLabel": "Organizacja integracji",
@ -135,6 +137,8 @@
"apiURLLabel": "Adres API", "apiURLLabel": "Adres API",
"apiURLHint": "Ustaw własny adres API", "apiURLHint": "Ustaw własny adres API",
"selectApiURL": "Adres API", "selectApiURL": "Adres API",
"experimentalUniversalPatchesLabel": "Wsparcie dla uniwersalnych eksperymentalnych łatek",
"experimentalUniversalPatchesHint": "Wyświetl wszystkie aplikacje do użycia z uniwersalnymi łatkami, ładowanie listy aplikacji może być wolniejsze",
"experimentalPatchesLabel": "Wsparcie dla eksperymentalnych łatek", "experimentalPatchesLabel": "Wsparcie dla eksperymentalnych łatek",
"experimentalPatchesHint": "Włącz użycie nieobsługiwanych łatek w dowolnej wersji aplikacji", "experimentalPatchesHint": "Włącz użycie nieobsługiwanych łatek w dowolnej wersji aplikacji",
"enabledExperimentalPatches": "Włączone wsparcie dla eksperymentalnych łatek", "enabledExperimentalPatches": "Włączone wsparcie dla eksperymentalnych łatek",
@ -150,16 +154,16 @@
"deleteTempDirLabel": "Usuń pliki tymczasowe", "deleteTempDirLabel": "Usuń pliki tymczasowe",
"deleteTempDirHint": "Usuń nieużywane pliki tymczasowe", "deleteTempDirHint": "Usuń nieużywane pliki tymczasowe",
"deletedTempDir": "Pliki tymczasowe zostały usunięte", "deletedTempDir": "Pliki tymczasowe zostały usunięte",
"exportPatchesLabel": "Eksportuj wybrane aktualizacje", "exportPatchesLabel": "Eksportuj wybrane łatki",
"exportPatchesHint": "Eksportuj wybrane aktualizacje do pliku JSON", "exportPatchesHint": "Eksportuj wybrane łatki do pliku JSON",
"exportedPatches": "Wyeksportowano wybrane aktualizacje", "exportedPatches": "Wyeksportowano wybór łatek",
"noExportFileFound": "Brak wybranych aktualizacji do wyeksportowania", "noExportFileFound": "Brak wybranych łatek do wyeksportowania",
"importPatchesLabel": "Importuj wybrane aktualizacje", "importPatchesLabel": "Importuj wybrane łatki",
"importPatchesHint": "Importuj wybrane aktualizacje z pliku JSON", "importPatchesHint": "Importuj wybrane łatki z pliku JSON",
"importedPatches": "Zaimportowano wybrane aktualizacje", "importedPatches": "Zaimportowano wybrane łatki",
"resetStoredPatchesLabel": "Zresetuj aktualizacje", "resetStoredPatchesLabel": "Resetuj łatki",
"resetStoredPatchesHint": "Zresetuj wybrane aktualizacje", "resetStoredPatchesHint": "Zresetuj wybrane przechowywane łatki",
"resetStoredPatches": "Wybrane aktualizacje zostały zresetowane", "resetStoredPatches": "Wybrane łatki zostały zresetowane",
"jsonSelectorErrorMessage": "Nie można użyć wybranego pliku JSON", "jsonSelectorErrorMessage": "Nie można użyć wybranego pliku JSON",
"deleteLogsLabel": "Usuń logi", "deleteLogsLabel": "Usuń logi",
"deleteLogsHint": "Usuń logi zebrane przez menadżera", "deleteLogsHint": "Usuń logi zebrane przez menadżera",
@ -178,17 +182,17 @@
"originalPackageNameLabel": "Oryginalna nazwa pakietu", "originalPackageNameLabel": "Oryginalna nazwa pakietu",
"installTypeLabel": "Typ instalacji", "installTypeLabel": "Typ instalacji",
"rootTypeLabel": "Root", "rootTypeLabel": "Root",
"nonRootTypeLabel": "Bez Roota", "nonRootTypeLabel": "Bez roota",
"patchedDateLabel": "Data modyfikacji", "patchedDateLabel": "Data modyfikacji",
"patchedDateHint": "{date} o godzinie {time}", "patchedDateHint": "{date} o godzinie {time}",
"appliedPatchesLabel": "Zastosowane aktualizacje", "appliedPatchesLabel": "Zastosowane łatki",
"appliedPatchesHint": "Zastosowano {quantity} aktualizacji", "appliedPatchesHint": "Zastosowano {quantity} łatek",
"updateNotImplemented": "Ta funkcja nie została jeszcze zaimplementowana" "updateNotImplemented": "Ta funkcja nie została jeszcze zaimplementowana"
}, },
"contributorsView": { "contributorsView": {
"widgetTitle": "Współtwórcy", "widgetTitle": "Współtwórcy",
"patcherContributors": "Współtwórcy patcher'a", "patcherContributors": "Współtwórcy patcher'a",
"patchesContributors": "Współtwórcy aktualizacji", "patchesContributors": "Współtwórcy łatek",
"integrationsContributors": "Współtwórcy integracji", "integrationsContributors": "Współtwórcy integracji",
"cliContributors": "Współtwórcy CLI", "cliContributors": "Współtwórcy CLI",
"managerContributors": "Współtwórcy menedżera" "managerContributors": "Współtwórcy menedżera"

200
assets/i18n/pt_PT.json Normal file
View File

@ -0,0 +1,200 @@
{
"okButton": "OK",
"cancelButton": "Cancelar",
"updateButton": "Atualizar",
"enabledLabel": "Ativado",
"disabledLabel": "Desativado",
"yesButton": "Sim",
"noButton": "Não",
"warning": "Aviso",
"navigationView": {
"dashboardTab": "Painel de controlo",
"patcherTab": "Patcher",
"settingsTab": "Definições"
},
"homeView": {
"widgetTitle": "Painel de controlo",
"updatesSubtitle": "Atualizações",
"patchedSubtitle": "Aplicações Modificadas",
"updatesAvailable": "Atualizações disponíveis",
"noUpdates": "Nenhuma atualização disponível",
"WIP": "Em desenvolvimento...",
"noInstallations": "Nenhuma aplicação modificada instalada",
"installed": "Instalado",
"updateDialogTitle": "Atualizar Manager",
"updateChangelogTitle": "Alterações",
"notificationTitle": "Atualização transferida",
"notificationText": "Toque para instalar a atualização",
"downloadingMessage": "A transferir a atualização...",
"installingMessage": "A instalar atualização...",
"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."
},
"applicationItem": {
"patchButton": "Modificar",
"infoButton": "Informação",
"changelogLabel": "Alterações"
},
"latestCommitCard": {
"loadingLabel": "A carregar...",
"timeagoLabel": "há {time}",
"patcherLabel": "Patcher: ",
"managerLabel": "Manager: ",
"updateButton": "Atualizar Manager"
},
"patcherView": {
"widgetTitle": "Patcher",
"patchButton": "Modificar",
"patchDialogText": "Selecionou uma modificação de recurso e uma instalação dividida de APK foi detetada, logo podem ocorrer erros de modificação.\nTem a certeza que deseja prosseguir?"
},
"appSelectorCard": {
"widgetTitle": "Selecione uma aplicação",
"widgetTitleSelected": "Aplicação selecionada",
"widgetSubtitle": "Nenhuma aplicação selecionada",
"noAppsLabel": "Não foram encontradas aplicações",
"currentVersion": "Atual",
"recommendedVersion": "Recomendada",
"anyVersion": "qualquer"
},
"patchSelectorCard": {
"widgetTitle": "Selecionar modificações",
"widgetTitleSelected": "Modificações selecionadas",
"widgetSubtitle": "Selecione uma aplicação primeiro",
"widgetEmptySubtitle": "Nenhuma modificação selecionada"
},
"socialMediaCard": {
"widgetTitle": "Redes sociais",
"widgetSubtitle": "Estamos online!"
},
"appSelectorView": {
"viewTitle": "Selecione uma aplicação",
"searchBarHint": "Procurar aplicações",
"storageButton": "Armazenamento",
"errorMessage": "Não é possível usar a aplicação selecionada"
},
"patchesSelectorView": {
"viewTitle": "Selecionar modificações",
"searchBarHint": "Procurar modificações",
"doneButton": "Concluído",
"recommended": "Recomendadas",
"all": "Todas",
"none": "Nenhuma",
"loadPatchesSelection": "Carregar seleção de modificações",
"noSavedPatches": "Nenhuma correção salva para o aplicativo selecionado.\nPressione \"Concluir\" para salvar a seleção atual.",
"noPatchesFound": "Nenhuma modificação encontrada para a aplicação selecionada",
"selectAllPatchesWarningContent": "Está prestes a selecionar todos os patches que incluem correções não recomendadas e podem causar comportamentos indesejados."
},
"patchItem": {
"unsupportedDialogText": "Selecionar esta modificação pode resultar em erros.\n\nVersão da aplicação: {packageVersion}\nVersões suportadas:\n{supportedVersions}",
"unsupportedPatchVersion": "Modificar não é suportado para esta versão da aplicação. Ativa a opção experimental nas definições para proceder."
},
"installerView": {
"widgetTitle": "Instalador",
"installButton": "Instalar",
"installRootButton": "Instalar como Root",
"openButton": "Abrir",
"shareButton": "Partilhar ficheiro",
"notificationTitle": "O ReVanced Manager está a fazer as modificações",
"notificationText": "Toca para voltar ao instalador",
"shareApkMenuOption": "Partilhar APK",
"exportApkMenuOption": "Exportar APK",
"shareLogMenuOption": "Partilhar registo",
"installErrorDialogTitle": "Erro",
"installErrorDialogText1": "A instalação com root não é possível com a seleção de modificações atuais.\nVolta a fazer a modificação da tua aplicação ou escolhe uma instalação sem root.",
"installErrorDialogText2": "A instalação sem root não é possível com a seleção de modificações atuais.\nVolta a fazer a modificação da tua aplicação ou escolhe uma instalação com root se o teu dispositivo estiver rooted.",
"installErrorDialogText3": "Não é possível instalar com root, pois o APK original foi selecionado do armazenamento.\nSelecione uma aplicação instalada ou escolha uma instalação sem root.",
"noExit": "O instalador ainda está em execução, não é possível sair..."
},
"settingsView": {
"widgetTitle": "Definições",
"appearanceSectionTitle": "Aparência",
"teamSectionTitle": "Equipa",
"infoSectionTitle": "Informação",
"advancedSectionTitle": "Opções avançadas",
"logsSectionTitle": "Registos",
"darkThemeLabel": "Modo escuro",
"darkThemeHint": "Bem-vindo ao lado negro",
"dynamicThemeLabel": "Material You",
"dynamicThemeHint": "Aproveite uma experiência mais próxima do tema do seu dispositivo",
"languageLabel": "Idioma",
"englishOption": "Inglês",
"sourcesLabel": "Fontes",
"sourcesLabelHint": "Configure as suas fontes personalizadas",
"hostRepositoryLabel": "API do Repositório",
"orgPatchesLabel": "Organização de Modificações",
"sourcesPatchesLabel": "Fonte das Modificações",
"orgIntegrationsLabel": "Organização de Integrações",
"sourcesIntegrationsLabel": "Fonte das Integrações",
"sourcesResetDialogTitle": "Repor",
"sourcesResetDialogText": "Tem certeza que deseja redefinir as fontes personalizadas para os seus valores padrão?",
"apiURLResetDialogText": "Tem certeza de que deseja repor o URL da API para o seu valor padrão?",
"contributorsLabel": "Contribuidores",
"contributorsHint": "Uma lista de contribuidores do ReVanced",
"logsLabel": "Registos",
"logsHint": "Compartilhar registos do Manager",
"apiURLLabel": "URL da API",
"apiURLHint": "Configura a tua URL de API personalizada",
"selectApiURL": "URL da API",
"experimentalUniversalPatchesLabel": "Suporte experimental para modificações universais",
"experimentalUniversalPatchesHint": "Mostrar todas as aplicações a usar com as modificações universais, a lista de carregamento de apps pode ser mais lenta",
"experimentalPatchesLabel": "Suporte para modificações experimentais",
"experimentalPatchesHint": "Ativa o uso de modificações não suportadas em qualquer versão das aplicações",
"enabledExperimentalPatches": "Suporte para modificações experimentais ativo",
"exportSectionTitle": "Importar e exportar",
"aboutLabel": "Sobre",
"snackbarMessage": "Copiado para a área de transferência",
"sentryLabel": "Registo do coletor de erros",
"sentryHint": "Enviar registos anónimos para nos ajudar a melhorar o ReVanced Manager",
"restartAppForChanges": "Reinicia a aplicação para aplicar as alterações",
"deleteKeystoreLabel": "Excluir keystore",
"deleteKeystoreHint": "Excluir a keystore usada para assinar a aplicação",
"deletedKeystore": "Keystore excluída",
"deleteTempDirLabel": "Apagar arquivos temporários",
"deleteTempDirHint": "Apagar arquivos temporários não utilizados",
"deletedTempDir": "Arquivos temporários apagados",
"exportPatchesLabel": "Exportar seleção de modificações",
"exportPatchesHint": "Exporta a seleção de modificações para um arquivo JSON",
"exportedPatches": "Seleção de modificações exportada",
"noExportFileFound": "Nenhuma seleção de modificações para exportar",
"importPatchesLabel": "Importar seleção de modificações",
"importPatchesHint": "Importa a seleção de modificações de um arquivo JSON",
"importedPatches": "Seleção de modificações importada",
"resetStoredPatchesLabel": "Redefinir modificações",
"resetStoredPatchesHint": "Redefinir a seleção de modificações armazenada",
"resetStoredPatches": "A seleção de modificações foi redefinida",
"jsonSelectorErrorMessage": "Não é possível usar o arquivo JSON selecionado",
"deleteLogsLabel": "Eliminar registos",
"deleteLogsHint": "Exclui registos do manager coletados",
"deletedLogs": "Registos excluídos"
},
"appInfoView": {
"widgetTitle": "Informações da aplicação",
"openButton": "Abrir",
"uninstallButton": "Desinstalar",
"patchButton": "Modificar",
"unpatchButton": "Desalterar",
"unpatchDialogText": "Tens a certeza que queres desalterar esta app?",
"rootDialogTitle": "Erro",
"rootDialogText": "A aplicação foi instalada com permissões de superutilizador, mas atualmente o ReVanced Manager não tem permissões.\nPor favor, conceda permissões de superutilizador primeiro.",
"packageNameLabel": "Nome do pacote",
"originalPackageNameLabel": "Nome original do pacote",
"installTypeLabel": "Tipo de instalação",
"rootTypeLabel": "Root",
"nonRootTypeLabel": "Sem root",
"patchedDateLabel": "Data da Modificação",
"patchedDateHint": "{date} às {time}",
"appliedPatchesLabel": "Modificações aplicadas",
"appliedPatchesHint": "{quantity} modificação/ões aplicada/s",
"updateNotImplemented": "Este recurso ainda não foi implementado"
},
"contributorsView": {
"widgetTitle": "Contribuidores",
"patcherContributors": "Contribuidores do Modificador",
"patchesContributors": "Contribuidores das Modificações",
"integrationsContributors": "Contribuidores das Integrações",
"cliContributors": "Contribuidores do CLI",
"managerContributors": "Contribuidores do Manager"
}
}

View File

@ -1,6 +1,7 @@
{ {
"okButton": "OK", "okButton": "OK",
"cancelButton": "Anulează", "cancelButton": "Anulează",
"updateButton": "Actualizare",
"enabledLabel": "Activat", "enabledLabel": "Activat",
"disabledLabel": "Dezactivat", "disabledLabel": "Dezactivat",
"yesButton": "Da", "yesButton": "Da",
@ -21,7 +22,7 @@
"noInstallations": "Nu sunt instalate aplicații patch-uite", "noInstallations": "Nu sunt instalate aplicații patch-uite",
"installed": "Instalat", "installed": "Instalat",
"updateDialogTitle": "Manager Actualizări", "updateDialogTitle": "Manager Actualizări",
"updateDialogText": "Sunteți sigur că doriți să descărcați și să actualizați ReVanced Manager?", "updateChangelogTitle": "Istoric schimbări",
"notificationTitle": "Actualizare descărcată", "notificationTitle": "Actualizare descărcată",
"notificationText": "Apăsați pentru a instala actualizarea", "notificationText": "Apăsați pentru a instala actualizarea",
"downloadingMessage": "Se descarcă actualizarea...", "downloadingMessage": "Se descarcă actualizarea...",
@ -81,13 +82,13 @@
"all": "Toate", "all": "Toate",
"none": "Niciunul", "none": "Niciunul",
"loadPatchesSelection": "Încărcați selecția de patch-uri", "loadPatchesSelection": "Încărcați selecția de patch-uri",
"noSavedPatches": "Nu există patch-uri salvate pentru aplicația selectată\nApăsați Terminat pentru a salva selecția curentă", "noSavedPatches": "Nu există patch-uri salvate pentru aplicația selectată.\nApăsați Terminat pentru a salva selecția curentă.",
"noPatchesFound": "Nu s-au găsit patch-uri pentru aplicația selectată", "noPatchesFound": "Nu s-au găsit patch-uri pentru aplicația selectată",
"selectAllPatchesWarningContent": "Urmează să selectați toate patch-urile, acestea incluzând patch-uri nerecomandate care pot cauza funcționarea necorespunzătoare a aplicației." "selectAllPatchesWarningContent": "Urmează să selectați toate patch-urile, acestea incluzând patch-uri nerecomandate care pot cauza funcționarea necorespunzătoare a aplicației."
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "Selectarea acestui patch poate rezulta în erori de patch-uire.\n\nVersiunea aplicației: {packageVersion}\nVersiuni compatibile:\n{supportedVersions}", "unsupportedDialogText": "Selectarea acestui patch poate rezulta în erori de patch-uire.\n\nVersiunea aplicației: {packageVersion}\nVersiuni compatibile:\n{supportedVersions}",
"unsupportedPatchVersion": "Patch-ul nu este compatibil cu versiunea curentă a aplicației.\nActivați comutatorul experimental din setări pentru a continua." "unsupportedPatchVersion": "Patch-ul nu este compatibil cu această versiune a aplicației. Activați comutatorul experimental din setări pentru a continua."
}, },
"installerView": { "installerView": {
"widgetTitle": "Instalator", "widgetTitle": "Instalator",
@ -121,6 +122,7 @@
"englishOption": "Engleză", "englishOption": "Engleză",
"sourcesLabel": "Surse", "sourcesLabel": "Surse",
"sourcesLabelHint": "Configurați-vă sursele personalizate", "sourcesLabelHint": "Configurați-vă sursele personalizate",
"hostRepositoryLabel": "Repository API",
"orgPatchesLabel": "Organizarea patch-urilor", "orgPatchesLabel": "Organizarea patch-urilor",
"sourcesPatchesLabel": "Sursă patch-uri", "sourcesPatchesLabel": "Sursă patch-uri",
"orgIntegrationsLabel": "Organizare integrări", "orgIntegrationsLabel": "Organizare integrări",
@ -135,6 +137,8 @@
"apiURLLabel": "API URL", "apiURLLabel": "API URL",
"apiURLHint": "Configurați-vă API URL-ul personalizat", "apiURLHint": "Configurați-vă API URL-ul personalizat",
"selectApiURL": "API URL", "selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "Suport pentru patch-uri experimentale universale",
"experimentalUniversalPatchesHint": "Afișează toate aplicațiile de utilizat cu patch-uri universale, lista de aplicații poate fi mai lentă",
"experimentalPatchesLabel": "Suport pentru patch-uri experimentale", "experimentalPatchesLabel": "Suport pentru patch-uri experimentale",
"experimentalPatchesHint": "Activați utilizarea de patch-uri nesuportate în orice versiune a aplicației", "experimentalPatchesHint": "Activați utilizarea de patch-uri nesuportate în orice versiune a aplicației",
"enabledExperimentalPatches": "Suport pentru patch-uri experimentale", "enabledExperimentalPatches": "Suport pentru patch-uri experimentale",
@ -148,7 +152,7 @@
"deleteKeystoreHint": "Ștergeți keystore-ul utilizat pentru a însemna aplicația", "deleteKeystoreHint": "Ștergeți keystore-ul utilizat pentru a însemna aplicația",
"deletedKeystore": "Keystore șters", "deletedKeystore": "Keystore șters",
"deleteTempDirLabel": "Ștergeți fișierele temporare", "deleteTempDirLabel": "Ștergeți fișierele temporare",
"deleteTempDirHint": "Ștergeți fișierele temporare neutilizate", "deleteTempDirHint": "Șterge fișierele temporare neutilizate",
"deletedTempDir": "Fișierele temporare au fost șterse", "deletedTempDir": "Fișierele temporare au fost șterse",
"exportPatchesLabel": "Exportă selecția patch-urilor", "exportPatchesLabel": "Exportă selecția patch-urilor",
"exportPatchesHint": "Exportă selecția patch-urilor într-un fișier JSON", "exportPatchesHint": "Exportă selecția patch-urilor într-un fișier JSON",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "ОК", "okButton": "ОК",
"cancelButton": "Отмена", "cancelButton": "Отмена",
"updateButton": "Обновить",
"enabledLabel": "Включено", "enabledLabel": "Включено",
"disabledLabel": "Выключено", "disabledLabel": "Выключено",
"yesButton": "Да", "yesButton": "Да",
@ -21,7 +22,7 @@
"noInstallations": "Не установлено пропатченных приложений", "noInstallations": "Не установлено пропатченных приложений",
"installed": "Установлено", "installed": "Установлено",
"updateDialogTitle": "Обновить Manager", "updateDialogTitle": "Обновить Manager",
"updateDialogText": "Вы уверены, что хотите загрузить и обновить ReVanced Manager?", "updateChangelogTitle": "Список изменений",
"notificationTitle": "Обновление загружено", "notificationTitle": "Обновление загружено",
"notificationText": "Нажмите, чтобы установить обновление", "notificationText": "Нажмите, чтобы установить обновление",
"downloadingMessage": "Скачивается обновление...", "downloadingMessage": "Скачивается обновление...",
@ -77,17 +78,17 @@
"viewTitle": "Выберите патчи", "viewTitle": "Выберите патчи",
"searchBarHint": "Искать патчи", "searchBarHint": "Искать патчи",
"doneButton": "Готово", "doneButton": "Готово",
"recommended": "Рекомендуемые", "recommended": "Рекомендуется",
"all": "Все", "all": "Все",
"none": "Никакие", "none": "Никакие",
"loadPatchesSelection": "Загрузить выбор патчей", "loadPatchesSelection": "Загрузить выбор патчей",
"noSavedPatches": "Нет сохранённых патчей для выбранного приложения\nНажмите «Готово» для сохранения текущего выбора", "noSavedPatches": "Нет сохранённых патчей для выбранного приложения.\nНажмите «Готово» для сохранения текущего выбора.",
"noPatchesFound": "Для выбранного приложения не найдены патчи", "noPatchesFound": "Для выбранного приложения не найдены патчи",
"selectAllPatchesWarningContent": "Вы собираетесь выбрать все патчи, в том числе и нерекомендуемые патчи, которые могут вызвать нежелательное поведение." "selectAllPatchesWarningContent": "Вы собираетесь выбрать все патчи, в том числе и нерекомендуемые патчи, которые могут вызвать нежелательное поведение."
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "Выбор этого патча может привести к ошибкам во время патча.\n\nВерсия приложения: {packageVersion}\nПоддерживаемые версии:\n{supportedVersions}", "unsupportedDialogText": "Выбор этого патча может привести к ошибкам во время патча.\n\nВерсия приложения: {packageVersion}\nПоддерживаемые версии:\n{supportedVersions}",
"unsupportedPatchVersion": "Патч не поддерживается этой версией приложения. Для продолжения включите опцию экспериментальной поддержки в настройках." "unsupportedPatchVersion": "Патч не поддерживается для этой версии приложения. Чтобы продолжить, включите опцию экспериментальной поддержки в настройках."
}, },
"installerView": { "installerView": {
"widgetTitle": "Установщик", "widgetTitle": "Установщик",
@ -101,7 +102,7 @@
"exportApkMenuOption": "Экспорт APK", "exportApkMenuOption": "Экспорт APK",
"shareLogMenuOption": "Отправить лог", "shareLogMenuOption": "Отправить лог",
"installErrorDialogTitle": "Ошибка", "installErrorDialogTitle": "Ошибка",
"installErrorDialogText1": "Root-установка невозможна с выбранными патчами.\nПерепатчите приложение, или выберите non-root установку.", "installErrorDialogText1": "Root-установка невозможна с выбранными патчами.\nПерепатчите приложение или выберите non-root установку.",
"installErrorDialogText2": "Non-root установка невозможна с выбранными патчами.\nПерепатчите приложение, или выберите root-установку, если на вашем устройстве есть root.", "installErrorDialogText2": "Non-root установка невозможна с выбранными патчами.\nПерепатчите приложение, или выберите root-установку, если на вашем устройстве есть root.",
"installErrorDialogText3": "Root-установка невозможна, так как оригинальный APK был выбран из хранилища.\nВыберите установленное приложение, или выберите non-root установку.", "installErrorDialogText3": "Root-установка невозможна, так как оригинальный APK был выбран из хранилища.\nВыберите установленное приложение, или выберите non-root установку.",
"noExit": "Установщик все еще запущен, выход невозможен..." "noExit": "Установщик все еще запущен, выход невозможен..."
@ -121,6 +122,7 @@
"englishOption": "Английский", "englishOption": "Английский",
"sourcesLabel": "Источники", "sourcesLabel": "Источники",
"sourcesLabelHint": "Настройте свои источники", "sourcesLabelHint": "Настройте свои источники",
"hostRepositoryLabel": "API репозитория",
"orgPatchesLabel": "Организация патчей", "orgPatchesLabel": "Организация патчей",
"sourcesPatchesLabel": "Репозиторий патчей", "sourcesPatchesLabel": "Репозиторий патчей",
"orgIntegrationsLabel": "Организация интеграций", "orgIntegrationsLabel": "Организация интеграций",
@ -135,6 +137,8 @@
"apiURLLabel": "API-ссылка", "apiURLLabel": "API-ссылка",
"apiURLHint": "Настройте свою API-ссылку", "apiURLHint": "Настройте свою API-ссылку",
"selectApiURL": "API-ссылка", "selectApiURL": "API-ссылка",
"experimentalUniversalPatchesLabel": "Экспериментальная поддержка универсальных патчей",
"experimentalUniversalPatchesHint": "Отображать все приложения, поддерживающие универсальные патчи; загрузка списка приложений может быть медленнее",
"experimentalPatchesLabel": "Экспериментальная поддержка патчей", "experimentalPatchesLabel": "Экспериментальная поддержка патчей",
"experimentalPatchesHint": "Разрешить использование несовместимых патчей в любой версии приложения", "experimentalPatchesHint": "Разрешить использование несовместимых патчей в любой версии приложения",
"enabledExperimentalPatches": "Экспериментальная поддержка патчей включена", "enabledExperimentalPatches": "Экспериментальная поддержка патчей включена",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "OK", "okButton": "OK",
"cancelButton": "Zrušiť", "cancelButton": "Zrušiť",
"updateButton": "Aktualizovať",
"enabledLabel": "Zapnuté", "enabledLabel": "Zapnuté",
"disabledLabel": "Vypnuté", "disabledLabel": "Vypnuté",
"yesButton": "Áno", "yesButton": "Áno",
@ -21,7 +22,7 @@
"noInstallations": "Žiadne zaplátané aplikácie nie sú nainštalované", "noInstallations": "Žiadne zaplátané aplikácie nie sú nainštalované",
"installed": "Nainštalované", "installed": "Nainštalované",
"updateDialogTitle": "Aktualizovať Manažéra", "updateDialogTitle": "Aktualizovať Manažéra",
"updateDialogText": "Ste si istý, že chcete stiahnuť a aktualizovať ReVanced manažér?", "updateChangelogTitle": "Zoznam zmien",
"notificationTitle": "Aktualizácia bola stiahnutá", "notificationTitle": "Aktualizácia bola stiahnutá",
"notificationText": "Klepnutím nainštalujete aktualizáciu", "notificationText": "Klepnutím nainštalujete aktualizáciu",
"downloadingMessage": "Sťahovanie aktualizácie...", "downloadingMessage": "Sťahovanie aktualizácie...",
@ -81,13 +82,13 @@
"all": "Všetky", "all": "Všetky",
"none": "Žiadne", "none": "Žiadne",
"loadPatchesSelection": "Načítať výber záplat", "loadPatchesSelection": "Načítať výber záplat",
"noSavedPatches": "Žiadne uložené záplaty pre vybranú aplikáciu\nStlačením tlačidla Hotovo uložíte aktuálny výber", "noSavedPatches": "Žiadne uložené záplaty pre vybranú aplikáciu\nStlačením tlačidla Hotovo uložíte aktuálny výber.",
"noPatchesFound": "Neboli nájdené žiadne záplaty pre zvolenú aplikáciu", "noPatchesFound": "Neboli nájdené žiadne záplaty pre zvolenú aplikáciu",
"selectAllPatchesWarningContent": "Týmto vyberiete všetky záplaty, aj tie ktoré nie sú odporúčané. Toto môže viesť k nežiadúcim účinkom." "selectAllPatchesWarningContent": "Týmto vyberiete všetky záplaty, aj tie ktoré nie sú odporúčané. Toto môže viesť k nežiadúcim účinkom."
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "Výber tejto záplaty môže spôsobiť chyby.\n\nVerzia aplikácie: {packageVersion}\nPodporované verzie:\n{supportedVersions}", "unsupportedDialogText": "Výber tejto záplaty môže spôsobiť chyby.\n\nVerzia aplikácie: {packageVersion}\nPodporované verzie:\n{supportedVersions}",
"unsupportedPatchVersion": "Záplata nie je pre túto verziu aplikácie podporovaná. Ak chcete pokračovať, povoľte v nastaveniach prepínač experimental." "unsupportedPatchVersion": "Záplata nie je pre túto verziu aplikácie podporovaná. Ak chcete pokračovať, povoľte v nastaveniach experimentálny prepínač."
}, },
"installerView": { "installerView": {
"widgetTitle": "Inštalátor", "widgetTitle": "Inštalátor",
@ -121,6 +122,7 @@
"englishOption": "Anglicky", "englishOption": "Anglicky",
"sourcesLabel": "Zdroje", "sourcesLabel": "Zdroje",
"sourcesLabelHint": "Nastavte vlastné zdroje", "sourcesLabelHint": "Nastavte vlastné zdroje",
"hostRepositoryLabel": "API repozitára",
"orgPatchesLabel": "Autor záplaty", "orgPatchesLabel": "Autor záplaty",
"sourcesPatchesLabel": "Zdroj záplaty", "sourcesPatchesLabel": "Zdroj záplaty",
"orgIntegrationsLabel": "Autor integrácie", "orgIntegrationsLabel": "Autor integrácie",
@ -135,6 +137,8 @@
"apiURLLabel": "API URL", "apiURLLabel": "API URL",
"apiURLHint": "Nastaviť vlastnú API URL", "apiURLHint": "Nastaviť vlastnú API URL",
"selectApiURL": "API URL", "selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "Experimentálna podpora univerzálnych záplat",
"experimentalUniversalPatchesHint": "Zobrazenie všetkých aplikácií na použitie s univerzálnymi záplatami, načítanie zoznamu aplikácií môže byť pomalšie",
"experimentalPatchesLabel": "Podpora experimentálnych záplat", "experimentalPatchesLabel": "Podpora experimentálnych záplat",
"experimentalPatchesHint": "Povoliť používanie nepodporovaných záplat v akejkoľvek verzii aplikácie", "experimentalPatchesHint": "Povoliť používanie nepodporovaných záplat v akejkoľvek verzii aplikácie",
"enabledExperimentalPatches": "Podpora experimentálnych záplat povolená", "enabledExperimentalPatches": "Podpora experimentálnych záplat povolená",

200
assets/i18n/sw_KE.json Normal file
View File

@ -0,0 +1,200 @@
{
"okButton": "Sawa",
"cancelButton": "Ghairi",
"updateButton": "Sasisho",
"enabledLabel": "Imewashwa",
"disabledLabel": "Imezimwa",
"yesButton": "Ndiyo",
"noButton": "Hapana",
"warning": "Onyo",
"navigationView": {
"dashboardTab": "Dashibodi",
"patcherTab": "Kirekebishaji",
"settingsTab": "Mpangilio"
},
"homeView": {
"widgetTitle": "Dashibodi",
"updatesSubtitle": "Sasisho",
"patchedSubtitle": "Programu zilizo na viraka",
"updatesAvailable": "Sasisho Zinapatikana",
"noUpdates": "Masasisho Hayapatikani",
"WIP": "Kazi inaendelea...",
"noInstallations": "Hakuna programu zilizo na viraka zilizosakinishwa",
"installed": "Imesakinishwa",
"updateDialogTitle": "Kidhibiti cha Usasishaji",
"updateChangelogTitle": "Changelog",
"notificationTitle": "Sasisho limepakuliwa",
"notificationText": "Gusa ili kusakinisha sasisho",
"downloadingMessage": "Inapakua sasisho...",
"installingMessage": "Inasakinisha sasisho...",
"errorDownloadMessage": "Imeshindwa kupakua sasisho",
"errorInstallMessage": "Imeshindwa kusakinisha sasisho",
"noConnection": "Hakuna muunganisho wa mtandao",
"updatesDisabled": "Kusasisha programu iliyobanwa kumezimwa kwa sasa. Rekebisha programu tena."
},
"applicationItem": {
"patchButton": "Rekebisha",
"infoButton": "Taarifa",
"changelogLabel": "Changelog"
},
"latestCommitCard": {
"loadingLabel": "Inapakia...",
"timeagoLabel": "{time} iliyopita",
"patcherLabel": "Kirekebishaji: ",
"managerLabel": "Meneja: ",
"updateButton": "Kidhibiti cha Usasishaji"
},
"patcherView": {
"widgetTitle": "Kirekebishaji",
"patchButton": "Rekebisha",
"patchDialogText": "Umechagua kiraka cha rasilimali na usakinishaji umegunduliwa ya split APK, kwa hivyo makosa ya kuweka viraka yanaweza kutokea.\nJe, una uhakika unataka kuendelea?"
},
"appSelectorCard": {
"widgetTitle": "Chagua programu",
"widgetTitleSelected": "Programu imechaguliwa",
"widgetSubtitle": "Hakuna programu iliyochaguliwa",
"noAppsLabel": "Hakuna programu zilizopatikana",
"currentVersion": "Sasa",
"recommendedVersion": "Imependekezwa",
"anyVersion": "yoyote"
},
"patchSelectorCard": {
"widgetTitle": "Chagua viraka",
"widgetTitleSelected": "Viraka vilivyochaguliwa",
"widgetSubtitle": "Chagua programu kwanza",
"widgetEmptySubtitle": "Hakuna viraka vilivyochaguliwa"
},
"socialMediaCard": {
"widgetTitle": "Mtandao wa kijamii",
"widgetSubtitle": "Tuko mtandaoni!"
},
"appSelectorView": {
"viewTitle": "Chagua programu",
"searchBarHint": "Tafuta programu",
"storageButton": "Hifadhi",
"errorMessage": "Haiwezi kutumia programu iliyochaguliwa"
},
"patchesSelectorView": {
"viewTitle": "Chagua viraka",
"searchBarHint": "Tafuta viraka",
"doneButton": "Imekamilika",
"recommended": "Imependekezwa",
"all": "Yote",
"none": "Hakuna",
"loadPatchesSelection": "Ingiza Uteuzi wa viraka",
"noSavedPatches": "Hakuna viraka vilivyohifadhiwa kwa programu iliyochaguliwa.\nBonyeza Nimemaliza ili kuhifadhi chaguo la sasa.",
"noPatchesFound": "Hakuna viraka vilivyopatikana kwa programu iliyochaguliwa",
"selectAllPatchesWarningContent": "Unakaribia kuchagua viraka vyote, vinavyojumuisha viraka visivyopendekezwa na vinaweza kusababisha tabia isiyotakikana."
},
"patchItem": {
"unsupportedDialogText": "Toleo la programu: {packageVersion}\nMatoleo yanayotumika:{supportedVersions}",
"unsupportedPatchVersion": "Kiraka hakitumiki kwa toleo hili la programu. Washa ugeuzaji wa majaribio katika mipangilio ili kuendelea."
},
"installerView": {
"widgetTitle": "Kisakinishi",
"installButton": "Sakinisha",
"installRootButton": "Sakinisha kama mtumiaji mkuu",
"openButton": "Fungua",
"shareButton": "Gawana faili",
"notificationTitle": "Revanced Manager inatia viraka",
"notificationText": "Gusa ili urudi kwa kisakinishi",
"shareApkMenuOption": "Gawa APK",
"exportApkMenuOption": "Hamisha APK",
"shareLogMenuOption": "Gawa jarida",
"installErrorDialogTitle": "Hitilafu",
"installErrorDialogText1": "Usakinishaji kama mtumiaji mkuu hauwezekani kwa uteuzi wa viraka vya sasa.",
"installErrorDialogText2": "Usakinishaji usio wa msimamizi hauwezekani kwa uteuzi wa sasa wa viraka.",
"installErrorDialogText3": "Usakinishaji kama mtumiaji mkuu hauwezekani kwansababu APK ule wa asili ilichaguliwa kutoka kwa hifadhi.\nChagua programu iliyosakinishwa au chagua usakinishaji usio wa msimamizi.",
"noExit": "Kisakinishi bado kinafanya kazi, hakiwezi kutoka..."
},
"settingsView": {
"widgetTitle": "Mipangilio",
"appearanceSectionTitle": "Mwonekano",
"teamSectionTitle": "Timu",
"infoSectionTitle": "Taarifa",
"advancedSectionTitle": "Ziada",
"logsSectionTitle": "Kumbukumbu",
"darkThemeLabel": "Hali ya giza",
"darkThemeHint": "Karibu katika upande wa giza",
"dynamicThemeLabel": "Material You",
"dynamicThemeHint": "Furahia matumizi karibu na kifaa chako",
"languageLabel": "Lugha",
"englishOption": "Kiingereza",
"sourcesLabel": "Vyanzo",
"sourcesLabelHint": "Sanidi vyanzo vyako maalum",
"hostRepositoryLabel": "HIfadhi ya API",
"orgPatchesLabel": "Shirika la viraka",
"sourcesPatchesLabel": "Chanzo cha viraka",
"orgIntegrationsLabel": "Shirika la ujumuishaji",
"sourcesIntegrationsLabel": "Chanzo cha ujumuishaji",
"sourcesResetDialogTitle": "Anzisha",
"sourcesResetDialogText": "Je, una uhakika unataka kuweka upya vyanzo maalum kwa thamani zao chaguomsingi?",
"apiURLResetDialogText": "Je, una uhakika unataka kuweka upya URL ya API hadi thamani yake chaguomsingi?",
"contributorsLabel": "Wachangiaji",
"contributorsHint": "Orodha ya wachangiaji wa Revanced",
"logsLabel": "Kumbukumbu",
"logsHint": "Gawa kumbukumbu za meneja",
"apiURLLabel": "API URL",
"apiURLHint": "Sanidi URL yako ya API maalum",
"selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "Usaidizi wa viraka vya majaribio kwa wote",
"experimentalUniversalPatchesHint": "Onyesha programu zote za kutumia na viraka vya ulimwengu wote, orodha ya upakiaji inaweza kuwa polepole",
"experimentalPatchesLabel": "Usaidizi wa viraka vya majaribio",
"experimentalPatchesHint": "Washa matumizi ya viraka visivyotumika katika toleo lolote la programu",
"enabledExperimentalPatches": "Usaidizi wa viraka vya majaribio umewashwa",
"exportSectionTitle": "Kuagiza na kuuza nje",
"aboutLabel": "Kuhusu",
"snackbarMessage": "Imenakiliwa kwenye ubao wa kunakili",
"sentryLabel": "Sentry logging",
"sentryHint": "Tuma kumbukumbu bila majina ili utusaidie kuboresha Revanced Manager",
"restartAppForChanges": "Anzisha tena programu ili kutekeleza mabadiliko",
"deleteKeystoreLabel": "Futa Duka la Ufunguo",
"deleteKeystoreHint": "Futa Duka la Ufunguo iliyotumika kusaini programu",
"deletedKeystore": "Duka la ufunguo imefutwa",
"deleteTempDirLabel": "Futa faili za muda",
"deleteTempDirHint": "Futa faili za muda ambazo hazijatumiwa",
"deletedTempDir": "Faili za muda zimefutwa",
"exportPatchesLabel": "Hamisha uteuzi wa viraka",
"exportPatchesHint": "Hamisha uteuzi wa viraka kwenye faili ya JSON",
"exportedPatches": "Uteuzi wa viraka umehamishwa",
"noExportFileFound": "Hakuna viraka vilivyochaguliwa kuhamishwa",
"importPatchesLabel": "Ingiza uteuzi wa viraka",
"importPatchesHint": "Ingiza uteuzi wa viraka kutoka kwa faili ya JSON",
"importedPatches": "Uteuzi wa viraka umeingizwa",
"resetStoredPatchesLabel": "Weka upya viraka",
"resetStoredPatchesHint": "Weka upya uteuzi wa viraka vilivyohifadhiwa",
"resetStoredPatches": "Uteuzi wa viraka umewekwa upya",
"jsonSelectorErrorMessage": "Imeshindwa kutumia faili ya JSON iliyochaguliwa",
"deleteLogsLabel": "Futa kumbukumbu",
"deleteLogsHint": "Futa kumbukumbu za wasimamizi zilizokusanywa",
"deletedLogs": "Kumbukumbu zimefutwa"
},
"appInfoView": {
"widgetTitle": "Maelezo ya programu",
"openButton": "Fungua",
"uninstallButton": "Sanidua",
"patchButton": "Rekebisha",
"unpatchButton": "Ondoa kibandiko",
"unpatchDialogText": "Je, una uhakika unataka kubandua programu hii?",
"rootDialogTitle": "Hitilafu",
"rootDialogText": "Programu ilisakinishwa kwa ruhusa za mtumiaji mkuu, lakini kwa sasa ReVanced Manager haina ruhusa..\nTafadhali toa ruhusa za mtumiaji mkuu kwanza.",
"packageNameLabel": "Jina la kifurushi",
"originalPackageNameLabel": "Jina halisi la kifurushi",
"installTypeLabel": "Aina ya usakinishaji",
"rootTypeLabel": "Root",
"nonRootTypeLabel": "Sio mtumiaji mkuu",
"patchedDateLabel": "Tarehe iliyowekwa viraka",
"patchedDateHint": "{date} katika {time}",
"appliedPatchesLabel": "Viraka vilivyotumika",
"appliedPatchesHint": "{quantity} viraka vilivyotumika",
"updateNotImplemented": "Kipengele hiki bado hakijatekelezwa"
},
"contributorsView": {
"widgetTitle": "Wachangiaji",
"patcherContributors": "Wachangiaji wa Kirekebishaji",
"patchesContributors": "Wachangiaji wa Viraka",
"integrationsContributors": "Wachangiaji wa ujumuishaji",
"cliContributors": "Wachangiaji wa CLI",
"managerContributors": "Wachangiaji wa meneja"
}
}

View File

@ -1,6 +1,7 @@
{ {
"okButton": "Tamam", "okButton": "Tamam",
"cancelButton": "İptal", "cancelButton": "İptal",
"updateButton": "Güncelleme",
"enabledLabel": "Etkinleştirildi", "enabledLabel": "Etkinleştirildi",
"disabledLabel": "Devre dışı", "disabledLabel": "Devre dışı",
"yesButton": "Evet", "yesButton": "Evet",
@ -21,7 +22,7 @@
"noInstallations": "Yamalanmış uygulama bulunmamaktadır", "noInstallations": "Yamalanmış uygulama bulunmamaktadır",
"installed": "Yüklendi", "installed": "Yüklendi",
"updateDialogTitle": "Manager'ı güncelle", "updateDialogTitle": "Manager'ı güncelle",
"updateDialogText": "ReVanced Manager'ın güncellemesini indirmek ve yüklemek istediğinize emin misiniz?", "updateChangelogTitle": "Değişiklik Notları",
"notificationTitle": "Güncelleme indirildi", "notificationTitle": "Güncelleme indirildi",
"notificationText": "Güncellemeyi yüklemek için dokunun", "notificationText": "Güncellemeyi yüklemek için dokunun",
"downloadingMessage": "Güncelleme indiriliyor...", "downloadingMessage": "Güncelleme indiriliyor...",
@ -40,7 +41,7 @@
"loadingLabel": "Yükleniyor...", "loadingLabel": "Yükleniyor...",
"timeagoLabel": "{time} önce", "timeagoLabel": "{time} önce",
"patcherLabel": "Yamalayıcı: ", "patcherLabel": "Yamalayıcı: ",
"managerLabel": "Manager: ", "managerLabel": "Yönetici: ",
"updateButton": "Manager'ı güncelle" "updateButton": "Manager'ı güncelle"
}, },
"patcherView": { "patcherView": {
@ -81,7 +82,7 @@
"all": "Hepsi", "all": "Hepsi",
"none": "Hiçbiri", "none": "Hiçbiri",
"loadPatchesSelection": "Yama seçimlerini yükle", "loadPatchesSelection": "Yama seçimlerini yükle",
"noSavedPatches": "Seçilen uygulama için kaydedilmiş yama yok\nMevcut seçimi kaydetmek için Tamam'a tıklayın", "noSavedPatches": "Seçilen uygulama için kaydedilmiş yama yok.\nMevcut seçimi kaydetmek için Tamam'a tıklayın.",
"noPatchesFound": "Seçili uygulama için yama bulunamadı", "noPatchesFound": "Seçili uygulama için yama bulunamadı",
"selectAllPatchesWarningContent": "Tavsiye edilmeyenler de dahil bütün yamaları seçmek üzeresiniz, bu istenmeyen davranışlara sebep olabilir." "selectAllPatchesWarningContent": "Tavsiye edilmeyenler de dahil bütün yamaları seçmek üzeresiniz, bu istenmeyen davranışlara sebep olabilir."
}, },
@ -121,6 +122,7 @@
"englishOption": "İngilizce", "englishOption": "İngilizce",
"sourcesLabel": "Kaynaklar", "sourcesLabel": "Kaynaklar",
"sourcesLabelHint": "Özel kaynaklarınızı yapılandırın", "sourcesLabelHint": "Özel kaynaklarınızı yapılandırın",
"hostRepositoryLabel": "Depo API",
"orgPatchesLabel": "Yama organizasyonu", "orgPatchesLabel": "Yama organizasyonu",
"sourcesPatchesLabel": "Yama kaynağı", "sourcesPatchesLabel": "Yama kaynağı",
"orgIntegrationsLabel": "Entegrasyon organizasyonu", "orgIntegrationsLabel": "Entegrasyon organizasyonu",
@ -135,6 +137,8 @@
"apiURLLabel": "API URL", "apiURLLabel": "API URL",
"apiURLHint": "Özel API URL'inizi yapılandırın", "apiURLHint": "Özel API URL'inizi yapılandırın",
"selectApiURL": "API URL", "selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "Deneysel evrensel yama desteği",
"experimentalUniversalPatchesHint": "Evrensel yamalarla kullanılacak tüm uygulamaları görüntüleyin, uygulamaların yüklenmesi daha yavaş olabilir",
"experimentalPatchesLabel": "Deneysel yama desteği", "experimentalPatchesLabel": "Deneysel yama desteği",
"experimentalPatchesHint": "Herhangi bir uygulama sürümünde desteklenmeyen yamaları kullanmayı etkinleştir", "experimentalPatchesHint": "Herhangi bir uygulama sürümünde desteklenmeyen yamaları kullanmayı etkinleştir",
"enabledExperimentalPatches": "Deneysel yama desteği etkin", "enabledExperimentalPatches": "Deneysel yama desteği etkin",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "Đồng ý", "okButton": "Đồng ý",
"cancelButton": "Hủy", "cancelButton": "Hủy",
"updateButton": "Cập nhật",
"enabledLabel": "Đã bật", "enabledLabel": "Đã bật",
"disabledLabel": "Đã tắt", "disabledLabel": "Đã tắt",
"yesButton": "Có", "yesButton": "Có",
@ -21,7 +22,7 @@
"noInstallations": "Không có ứng dụng đã vá nào được cài đặt", "noInstallations": "Không có ứng dụng đã vá nào được cài đặt",
"installed": "Đã cài đặt", "installed": "Đã cài đặt",
"updateDialogTitle": "Update Manager", "updateDialogTitle": "Update Manager",
"updateDialogText": "Bạn có muốn tải về và cập nhật ReVanced Manager?", "updateChangelogTitle": "Nhật ký thay đổi",
"notificationTitle": "Cập nhật đã được tải xong", "notificationTitle": "Cập nhật đã được tải xong",
"notificationText": "Nhấn để cài đặt bản cập nhật", "notificationText": "Nhấn để cài đặt bản cập nhật",
"downloadingMessage": "Đang tải xuống bản cập nhật...", "downloadingMessage": "Đang tải xuống bản cập nhật...",
@ -81,7 +82,7 @@
"all": "Tất cả", "all": "Tất cả",
"none": "Không có", "none": "Không có",
"loadPatchesSelection": "Nạp các bản vá được chọn", "loadPatchesSelection": "Nạp các bản vá được chọn",
"noSavedPatches": "Không có bản vá cho ứng dụng được chọn\nNhấn Hoàn tất để lưu lựa chọn hiện tại", "noSavedPatches": "Không có bản vá cho ứng dụng được chọn\nNhấn Hoàn tất để lưu lựa chọn hiện tại.",
"noPatchesFound": "Không tìm thấy bản vá cho ứng dụng đã chọn", "noPatchesFound": "Không tìm thấy bản vá cho ứng dụng đã chọn",
"selectAllPatchesWarningContent": "Bạn sắp lựa chọn tất cả bản vá, bao gồm những bản vá không được khuyến nghị và có thể gây ra những hành vi không mong muốn." "selectAllPatchesWarningContent": "Bạn sắp lựa chọn tất cả bản vá, bao gồm những bản vá không được khuyến nghị và có thể gây ra những hành vi không mong muốn."
}, },
@ -121,6 +122,7 @@
"englishOption": "Tiếng Anh", "englishOption": "Tiếng Anh",
"sourcesLabel": "Nguồn", "sourcesLabel": "Nguồn",
"sourcesLabelHint": "Cấu hình nguồn tùy chỉnh của bạn", "sourcesLabelHint": "Cấu hình nguồn tùy chỉnh của bạn",
"hostRepositoryLabel": "API Kho lưu trữ",
"orgPatchesLabel": "Tác giả bản vá", "orgPatchesLabel": "Tác giả bản vá",
"sourcesPatchesLabel": "Nguồn bản vá", "sourcesPatchesLabel": "Nguồn bản vá",
"orgIntegrationsLabel": "Tác giá bản tích hợp", "orgIntegrationsLabel": "Tác giá bản tích hợp",
@ -135,6 +137,8 @@
"apiURLLabel": "Địa chỉ URL của API", "apiURLLabel": "Địa chỉ URL của API",
"apiURLHint": "Cấu hình API URL tùy chỉnh của bạn", "apiURLHint": "Cấu hình API URL tùy chỉnh của bạn",
"selectApiURL": "Địa chỉ URL của API", "selectApiURL": "Địa chỉ URL của API",
"experimentalUniversalPatchesLabel": "Hỗ trợ các bản vá thử nghiệm phổ biến",
"experimentalUniversalPatchesHint": "Hiển thị tất cả các ứng dụng dùng với các bản vá phổ biến, việc tải các danh sách ứng dụng có thể chậm hơn",
"experimentalPatchesLabel": "Hỗ trợ các bản vá thử nghiệm", "experimentalPatchesLabel": "Hỗ trợ các bản vá thử nghiệm",
"experimentalPatchesHint": "Bật sử dụng các bản vá không được hỗ trợ trong bất kỳ phiên bản nào của ứng dụng", "experimentalPatchesHint": "Bật sử dụng các bản vá không được hỗ trợ trong bất kỳ phiên bản nào của ứng dụng",
"enabledExperimentalPatches": "Đã bật hỗ trợ các bản vá thử nghiệm", "enabledExperimentalPatches": "Đã bật hỗ trợ các bản vá thử nghiệm",

View File

@ -1,6 +1,7 @@
{ {
"okButton": "確定", "okButton": "確定",
"cancelButton": "取消", "cancelButton": "取消",
"updateButton": "更新",
"enabledLabel": "已啟用", "enabledLabel": "已啟用",
"disabledLabel": "已停用", "disabledLabel": "已停用",
"yesButton": "是", "yesButton": "是",
@ -21,7 +22,7 @@
"noInstallations": "沒有安裝已修補的應用程式", "noInstallations": "沒有安裝已修補的應用程式",
"installed": "已安裝", "installed": "已安裝",
"updateDialogTitle": "更新 Manager", "updateDialogTitle": "更新 Manager",
"updateDialogText": "確定要下載並更新 ReVanced Manager 嗎?", "updateChangelogTitle": "更新日誌",
"notificationTitle": "已下載更新", "notificationTitle": "已下載更新",
"notificationText": "輕觸以安裝更新", "notificationText": "輕觸以安裝更新",
"downloadingMessage": "正在下載更新...", "downloadingMessage": "正在下載更新...",
@ -81,13 +82,13 @@
"all": "全部", "all": "全部",
"none": "無", "none": "無",
"loadPatchesSelection": "載入修補檔選取", "loadPatchesSelection": "載入修補檔選取",
"noSavedPatches": "選取的應用程式尚無已儲存的修補檔\n按下 [完成] 以儲存目前選取", "noSavedPatches": "選取的應用程式尚無已儲存的修補檔\n按下 [完成] 以儲存目前選取",
"noPatchesFound": "找不到適合所選應用程式的修補檔", "noPatchesFound": "找不到適合所選應用程式的修補檔",
"selectAllPatchesWarningContent": "即將選取所有修補檔——之中包含不推薦的修補檔,且可能造成非預期的結果。" "selectAllPatchesWarningContent": "即將選取所有修補檔——之中包含不推薦的修補檔,且可能造成非預期的結果。"
}, },
"patchItem": { "patchItem": {
"unsupportedDialogText": "選取此修補檔可能導致修補錯誤。\n應用程式版本 {packageVersion}\n支援的版本 {supportedVersions}", "unsupportedDialogText": "選取此修補檔可能導致修補錯誤。\n應用程式版本 {packageVersion}\n支援的版本 {supportedVersions}",
"unsupportedPatchVersion": "不支援此應用程式的版本的修補作業。請在設定中啟用實驗性選項以繼續進行。" "unsupportedPatchVersion": "修補檔不支援這個版本的應用程式。需啟用設定中實驗性選項才可繼續下一步。"
}, },
"installerView": { "installerView": {
"widgetTitle": "安裝程式", "widgetTitle": "安裝程式",
@ -121,6 +122,7 @@
"englishOption": "English", "englishOption": "English",
"sourcesLabel": "來源", "sourcesLabel": "來源",
"sourcesLabelHint": "設定自訂來源", "sourcesLabelHint": "設定自訂來源",
"hostRepositoryLabel": "儲存庫 API",
"orgPatchesLabel": "修補檔組織", "orgPatchesLabel": "修補檔組織",
"sourcesPatchesLabel": "修補檔來源", "sourcesPatchesLabel": "修補檔來源",
"orgIntegrationsLabel": "整合組織", "orgIntegrationsLabel": "整合組織",
@ -135,6 +137,8 @@
"apiURLLabel": "API URL", "apiURLLabel": "API URL",
"apiURLHint": "設定自訂 API URL", "apiURLHint": "設定自訂 API URL",
"selectApiURL": "API URL", "selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "支援實驗性通用型修補檔",
"experimentalUniversalPatchesHint": "顯示所有可使用通用型修補檔的應用程式,載入應用程式清單的時長可能會變得更慢",
"experimentalPatchesLabel": "支援實驗性修補檔", "experimentalPatchesLabel": "支援實驗性修補檔",
"experimentalPatchesHint": "啟用以在任何應用程式版本中使用不支援的修補檔", "experimentalPatchesHint": "啟用以在任何應用程式版本中使用不支援的修補檔",
"enabledExperimentalPatches": "已啟用支援實驗性修補檔", "enabledExperimentalPatches": "已啟用支援實驗性修補檔",
@ -183,7 +187,7 @@
"patchedDateHint": "{date} {time}", "patchedDateHint": "{date} {time}",
"appliedPatchesLabel": "已套用修補檔", "appliedPatchesLabel": "已套用修補檔",
"appliedPatchesHint": "已套用 {quantity} 個修補檔", "appliedPatchesHint": "已套用 {quantity} 個修補檔",
"updateNotImplemented": "這項功能還沒有實現" "updateNotImplemented": "這項功能尚未實作"
}, },
"contributorsView": { "contributorsView": {
"widgetTitle": "貢獻者", "widgetTitle": "貢獻者",

View File

@ -1,3 +1,5 @@
import 'dart:developer';
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:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
@ -10,9 +12,9 @@ import 'package:revanced_manager/services/revanced_api.dart';
import 'package:revanced_manager/ui/theme/dynamic_theme_builder.dart'; import 'package:revanced_manager/ui/theme/dynamic_theme_builder.dart';
import 'package:revanced_manager/ui/views/navigation/navigation_view.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_view.dart';
import 'package:revanced_manager/utils/environment.dart'; import 'package:revanced_manager/utils/environment.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked_themes/stacked_themes.dart'; import 'package:stacked_themes/stacked_themes.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/data/latest.dart' as tz;
late SharedPreferences prefs; late SharedPreferences prefs;
@ -21,11 +23,11 @@ Future main() async {
await setupLocator(); await setupLocator();
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await locator<ManagerAPI>().initialize(); await locator<ManagerAPI>().initialize();
String apiUrl = locator<ManagerAPI>().getApiUrl(); final String apiUrl = locator<ManagerAPI>().getApiUrl();
await locator<RevancedAPI>().initialize(apiUrl); await locator<RevancedAPI>().initialize(apiUrl);
await locator<CrowdinAPI>().initialize(); await locator<CrowdinAPI>().initialize();
bool isSentryEnabled = locator<ManagerAPI>().isSentryEnabled(); final bool isSentryEnabled = locator<ManagerAPI>().isSentryEnabled();
String repoUrl = locator<ManagerAPI>().getRepoUrl(); final String repoUrl = locator<ManagerAPI>().getRepoUrl();
locator<GithubAPI>().initialize(repoUrl); locator<GithubAPI>().initialize(repoUrl);
await locator<PatcherAPI>().initialize(); await locator<PatcherAPI>().initialize();
tz.initializeTimeZones(); tz.initializeTimeZones();
@ -65,7 +67,7 @@ class MyApp extends StatelessWidget {
// String replaceLocale = rawLocale.replaceAll('_', '-'); // String replaceLocale = rawLocale.replaceAll('_', '-');
// List<String> localeList = replaceLocale.split('-'); // List<String> localeList = replaceLocale.split('-');
// Locale locale = Locale(localeList[0], localeList[1]); // Locale locale = Locale(localeList[0], localeList[1]);
Locale locale = const Locale('en', 'US'); const Locale locale = Locale('en', 'US');
return DynamicThemeBuilder( return DynamicThemeBuilder(
title: 'ReVanced Manager', title: 'ReVanced Manager',
@ -79,8 +81,9 @@ class MyApp extends StatelessWidget {
useCountryCode: true, useCountryCode: true,
), ),
missingTranslationHandler: (key, locale) { missingTranslationHandler: (key, locale) {
print( log(
'--> Missing translation: key: $key, languageCode: ${locale?.languageCode}'); '--> Missing translation: key: $key, languageCode: ${locale?.languageCode}',
);
}, },
), ),
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,

View File

@ -5,13 +5,6 @@ part 'patch.g.dart';
@JsonSerializable() @JsonSerializable()
class Patch { class Patch {
final String name;
final String description;
final String version;
final bool excluded;
final List<String> dependencies;
final List<Package> compatiblePackages;
Patch({ Patch({
required this.name, required this.name,
required this.description, required this.description,
@ -22,6 +15,12 @@ class Patch {
}); });
factory Patch.fromJson(Map<String, dynamic> json) => _$PatchFromJson(json); factory Patch.fromJson(Map<String, dynamic> json) => _$PatchFromJson(json);
final String name;
final String description;
final String version;
final bool excluded;
final List<String> dependencies;
final List<Package> compatiblePackages;
Map<String, dynamic> toJson() => _$PatchToJson(this); Map<String, dynamic> toJson() => _$PatchToJson(this);
@ -37,9 +36,6 @@ class Patch {
@JsonSerializable() @JsonSerializable()
class Package { class Package {
final String name;
final List<String> versions;
Package({ Package({
required this.name, required this.name,
required this.versions, required this.versions,
@ -47,6 +43,8 @@ class Package {
factory Package.fromJson(Map<String, dynamic> json) => factory Package.fromJson(Map<String, dynamic> json) =>
_$PackageFromJson(json); _$PackageFromJson(json);
final String name;
final List<String> versions;
Map toJson() => _$PackageToJson(this); Map toJson() => _$PackageToJson(this);
} }

View File

@ -6,23 +6,6 @@ part 'patched_application.g.dart';
@JsonSerializable() @JsonSerializable()
class PatchedApplication { class PatchedApplication {
String name;
String packageName;
String originalPackageName;
String version;
final String apkFilePath;
@JsonKey(
fromJson: decodeBase64,
toJson: encodeBase64,
)
Uint8List icon;
DateTime patchDate;
bool isRooted;
bool isFromStorage;
bool hasUpdates;
List<String> appliedPatches;
List<String> changelog;
PatchedApplication({ PatchedApplication({
required this.name, required this.name,
required this.packageName, required this.packageName,
@ -40,6 +23,22 @@ class PatchedApplication {
factory PatchedApplication.fromJson(Map<String, dynamic> json) => factory PatchedApplication.fromJson(Map<String, dynamic> json) =>
_$PatchedApplicationFromJson(json); _$PatchedApplicationFromJson(json);
String name;
String packageName;
String originalPackageName;
String version;
final String apkFilePath;
@JsonKey(
fromJson: decodeBase64,
toJson: encodeBase64,
)
Uint8List icon;
DateTime patchDate;
bool isRooted;
bool isFromStorage;
bool hasUpdates;
List<String> appliedPatches;
List<String> changelog;
Map<String, dynamic> toJson() => _$PatchedApplicationToJson(this); Map<String, dynamic> toJson() => _$PatchedApplicationToJson(this);

View File

@ -13,9 +13,11 @@ class CrowdinAPI {
Future<void> initialize() async { Future<void> initialize() async {
try { try {
_dio = Dio(BaseOptions( _dio = Dio(
baseUrl: 'https://api.crowdin.com/api/v2', BaseOptions(
)); baseUrl: 'https://api.crowdin.com/api/v2',
),
);
_dio.interceptors.add(_dioCacheManager.interceptor); _dio.interceptors.add(_dioCacheManager.interceptor);
_dio.addSentry( _dio.addSentry(
@ -36,7 +38,7 @@ class CrowdinAPI {
Future<List> getLanguages() async { Future<List> getLanguages() async {
try { try {
var response = await _dio.get( final response = await _dio.get(
'/projects', '/projects',
options: buildCacheOptions( options: buildCacheOptions(
const Duration(hours: 6), const Duration(hours: 6),
@ -49,7 +51,7 @@ class CrowdinAPI {
), ),
), ),
); );
List targetLanguages = final List targetLanguages =
await response.data['data'][0]['data']['targetLanguages']; await response.data['data'][0]['data']['targetLanguages'];
return targetLanguages; return targetLanguages;

View File

@ -1,13 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio_http_cache_lts/dio_http_cache_lts.dart'; import 'package:dio_http_cache_lts/dio_http_cache_lts.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_dio/sentry_dio.dart'; import 'package:sentry_dio/sentry_dio.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
@lazySingleton @lazySingleton
class GithubAPI { class GithubAPI {
@ -28,11 +29,13 @@ class GithubAPI {
'com.spotify.music': 'spotify', 'com.spotify.music': 'spotify',
}; };
void initialize(String repoUrl) async { Future<void> initialize(String repoUrl) async {
try { try {
_dio = Dio(BaseOptions( _dio = Dio(
baseUrl: repoUrl, BaseOptions(
)); baseUrl: repoUrl,
),
);
_dio.interceptors.add(_dioCacheManager.interceptor); _dio.interceptors.add(_dioCacheManager.interceptor);
_dio.addSentry( _dio.addSentry(
@ -53,7 +56,7 @@ class GithubAPI {
Future<Map<String, dynamic>?> getLatestRelease(String repoName) async { Future<Map<String, dynamic>?> getLatestRelease(String repoName) async {
try { try {
var response = await _dio.get( final response = await _dio.get(
'/repos/$repoName/releases', '/repos/$repoName/releases',
options: _cacheOptions, options: _cacheOptions,
); );
@ -69,10 +72,10 @@ class GithubAPI {
String repoName, String repoName,
DateTime since, DateTime since,
) async { ) async {
String path = final String path =
'src/main/kotlin/app/revanced/patches/${repoAppPath[packageName]}'; 'src/main/kotlin/app/revanced/patches/${repoAppPath[packageName]}';
try { try {
var response = await _dio.get( final response = await _dio.get(
'/repos/$repoName/commits', '/repos/$repoName/commits',
queryParameters: { queryParameters: {
'path': path, 'path': path,
@ -80,7 +83,7 @@ class GithubAPI {
}, },
options: _cacheOptions, options: _cacheOptions,
); );
List<dynamic> commits = response.data; final List<dynamic> commits = response.data;
return commits return commits
.map( .map(
(commit) => (commit['commit']['message']).split('\n')[0] + (commit) => (commit['commit']['message']).split('\n')[0] +
@ -97,9 +100,9 @@ class GithubAPI {
Future<File?> getLatestReleaseFile(String extension, String repoName) async { Future<File?> getLatestReleaseFile(String extension, String repoName) async {
try { try {
Map<String, dynamic>? release = await getLatestRelease(repoName); final Map<String, dynamic>? release = await getLatestRelease(repoName);
if (release != null) { if (release != null) {
Map<String, dynamic>? asset = final Map<String, dynamic>? asset =
(release['assets'] as List<dynamic>).firstWhereOrNull( (release['assets'] as List<dynamic>).firstWhereOrNull(
(asset) => (asset['name'] as String).endsWith(extension), (asset) => (asset['name'] as String).endsWith(extension),
); );
@ -119,9 +122,9 @@ class GithubAPI {
Future<List<Patch>> getPatches(String repoName) async { Future<List<Patch>> getPatches(String repoName) async {
List<Patch> patches = []; List<Patch> patches = [];
try { try {
File? f = await getLatestReleaseFile('.json', repoName); final File? f = await getLatestReleaseFile('.json', repoName);
if (f != null) { if (f != null) {
List<dynamic> list = jsonDecode(f.readAsStringSync()); final List<dynamic> list = jsonDecode(f.readAsStringSync());
patches = list.map((patch) => Patch.fromJson(patch)).toList(); patches = list.map((patch) => Patch.fromJson(patch)).toList();
} }
} on Exception catch (e, s) { } on Exception catch (e, s) {
@ -133,7 +136,7 @@ class GithubAPI {
Future<String> getLastestReleaseVersion(String repoName) async { Future<String> getLastestReleaseVersion(String repoName) async {
try { try {
Map<String, dynamic>? release = await getLatestRelease(repoName); final Map<String, dynamic>? release = await getLatestRelease(repoName);
if (release != null) { if (release != null) {
return release['tag_name']; return release['tag_name'];
} else { } else {

View File

@ -20,8 +20,8 @@ class ManagerAPI {
final RootAPI _rootAPI = RootAPI(); final RootAPI _rootAPI = RootAPI();
final String patcherRepo = 'revanced-patcher'; final String patcherRepo = 'revanced-patcher';
final String cliRepo = 'revanced-cli'; final String cliRepo = 'revanced-cli';
late String storedPatchesFile = '/selected-patches.json';
late SharedPreferences _prefs; late SharedPreferences _prefs;
String storedPatchesFile = '/selected-patches.json';
String defaultApiUrl = 'https://releases.revanced.app/'; String defaultApiUrl = 'https://releases.revanced.app/';
String defaultRepoUrl = 'https://api.github.com'; String defaultRepoUrl = 'https://api.github.com';
String defaultPatcherRepo = 'revanced/revanced-patcher'; String defaultPatcherRepo = 'revanced/revanced-patcher';
@ -131,14 +131,15 @@ class ManagerAPI {
Future<void> deleteKeystore() async { Future<void> deleteKeystore() async {
final File keystore = File( final File keystore = File(
'/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore'); '/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore',
);
if (await keystore.exists()) { if (await keystore.exists()) {
await keystore.delete(); await keystore.delete();
} }
} }
List<PatchedApplication> getPatchedApps() { List<PatchedApplication> getPatchedApps() {
List<String> apps = _prefs.getStringList('patchedApps') ?? []; final List<String> apps = _prefs.getStringList('patchedApps') ?? [];
return apps.map((a) => PatchedApplication.fromJson(jsonDecode(a))).toList(); return apps.map((a) => PatchedApplication.fromJson(jsonDecode(a))).toList();
} }
@ -146,14 +147,16 @@ class ManagerAPI {
if (patchedApps.length > 1) { if (patchedApps.length > 1) {
patchedApps.sort((a, b) => a.name.compareTo(b.name)); patchedApps.sort((a, b) => a.name.compareTo(b.name));
} }
await _prefs.setStringList('patchedApps', await _prefs.setStringList(
patchedApps.map((a) => json.encode(a.toJson())).toList()); 'patchedApps',
patchedApps.map((a) => json.encode(a.toJson())).toList(),
);
} }
Future<void> savePatchedApp(PatchedApplication app) async { Future<void> savePatchedApp(PatchedApplication app) async {
List<PatchedApplication> patchedApps = getPatchedApps(); final List<PatchedApplication> patchedApps = getPatchedApps();
patchedApps.removeWhere((a) => a.packageName == app.packageName); patchedApps.removeWhere((a) => a.packageName == app.packageName);
ApplicationWithIcon? installed = await DeviceApps.getApp( final ApplicationWithIcon? installed = await DeviceApps.getApp(
app.packageName, app.packageName,
true, true,
) as ApplicationWithIcon?; ) as ApplicationWithIcon?;
@ -167,12 +170,12 @@ class ManagerAPI {
} }
Future<void> deletePatchedApp(PatchedApplication app) async { Future<void> deletePatchedApp(PatchedApplication app) async {
List<PatchedApplication> patchedApps = getPatchedApps(); final List<PatchedApplication> patchedApps = getPatchedApps();
patchedApps.removeWhere((a) => a.packageName == app.packageName); patchedApps.removeWhere((a) => a.packageName == app.packageName);
await setPatchedApps(patchedApps); await setPatchedApps(patchedApps);
} }
void clearAllData() async { Future<void> clearAllData() async {
try { try {
_revancedAPI.clearAllCache(); _revancedAPI.clearAllCache();
_githubAPI.clearAllCache(); _githubAPI.clearAllCache();
@ -187,7 +190,7 @@ class ManagerAPI {
Future<List<Patch>> getPatches() async { Future<List<Patch>> getPatches() async {
try { try {
String repoName = getPatchesRepo(); final String repoName = getPatchesRepo();
if (repoName == defaultPatchesRepo) { if (repoName == defaultPatchesRepo) {
return await _revancedAPI.getPatches(); return await _revancedAPI.getPatches();
} else { } else {
@ -201,7 +204,7 @@ class ManagerAPI {
Future<File?> downloadPatches() async { Future<File?> downloadPatches() async {
try { try {
String repoName = getPatchesRepo(); final String repoName = getPatchesRepo();
if (repoName == defaultPatchesRepo) { if (repoName == defaultPatchesRepo) {
return await _revancedAPI.getLatestReleaseFile( return await _revancedAPI.getLatestReleaseFile(
'.jar', '.jar',
@ -218,7 +221,7 @@ class ManagerAPI {
Future<File?> downloadIntegrations() async { Future<File?> downloadIntegrations() async {
try { try {
String repoName = getIntegrationsRepo(); final String repoName = getIntegrationsRepo();
if (repoName == defaultIntegrationsRepo) { if (repoName == defaultIntegrationsRepo) {
return await _revancedAPI.getLatestReleaseFile( return await _revancedAPI.getLatestReleaseFile(
'.apk', '.apk',
@ -260,16 +263,16 @@ class ManagerAPI {
} }
Future<String> getCurrentManagerVersion() async { Future<String> getCurrentManagerVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform(); final PackageInfo packageInfo = await PackageInfo.fromPlatform();
return packageInfo.version; return packageInfo.version;
} }
Future<List<PatchedApplication>> getAppsToRemove( Future<List<PatchedApplication>> getAppsToRemove(
List<PatchedApplication> patchedApps, List<PatchedApplication> patchedApps,
) async { ) async {
List<PatchedApplication> toRemove = []; final List<PatchedApplication> toRemove = [];
for (PatchedApplication app in patchedApps) { for (final PatchedApplication app in patchedApps) {
bool isRemove = await isAppUninstalled(app); final bool isRemove = await isAppUninstalled(app);
if (isRemove) { if (isRemove) {
toRemove.add(app); toRemove.add(app);
} }
@ -280,13 +283,13 @@ class ManagerAPI {
Future<List<PatchedApplication>> getUnsavedApps( Future<List<PatchedApplication>> getUnsavedApps(
List<PatchedApplication> patchedApps, List<PatchedApplication> patchedApps,
) async { ) async {
List<PatchedApplication> unsavedApps = []; final List<PatchedApplication> unsavedApps = [];
bool hasRootPermissions = await _rootAPI.hasRootPermissions(); final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) { if (hasRootPermissions) {
List<String> installedApps = await _rootAPI.getInstalledApps(); final List<String> installedApps = await _rootAPI.getInstalledApps();
for (String packageName in installedApps) { for (final String packageName in installedApps) {
if (!patchedApps.any((app) => app.packageName == packageName)) { if (!patchedApps.any((app) => app.packageName == packageName)) {
ApplicationWithIcon? application = await DeviceApps.getApp( final ApplicationWithIcon? application = await DeviceApps.getApp(
packageName, packageName,
true, true,
) as ApplicationWithIcon?; ) as ApplicationWithIcon?;
@ -307,15 +310,13 @@ class ManagerAPI {
} }
} }
} }
List<Application> userApps = await DeviceApps.getInstalledApplications( final List<Application> userApps =
includeSystemApps: false, await DeviceApps.getInstalledApplications();
includeAppIcons: false, for (final Application app in userApps) {
);
for (Application app in userApps) {
if (app.packageName.startsWith('app.revanced') && if (app.packageName.startsWith('app.revanced') &&
!app.packageName.startsWith('app.revanced.manager.') && !app.packageName.startsWith('app.revanced.manager.') &&
!patchedApps.any((uapp) => uapp.packageName == app.packageName)) { !patchedApps.any((uapp) => uapp.packageName == app.packageName)) {
ApplicationWithIcon? application = await DeviceApps.getApp( final ApplicationWithIcon? application = await DeviceApps.getApp(
app.packageName, app.packageName,
true, true,
) as ApplicationWithIcon?; ) as ApplicationWithIcon?;
@ -329,7 +330,6 @@ class ManagerAPI {
apkFilePath: application.apkFilePath, apkFilePath: application.apkFilePath,
icon: application.icon, icon: application.icon,
patchDate: DateTime.now(), patchDate: DateTime.now(),
isRooted: false,
), ),
); );
} }
@ -339,24 +339,27 @@ class ManagerAPI {
} }
Future<void> reAssessSavedApps() async { Future<void> reAssessSavedApps() async {
List<PatchedApplication> patchedApps = getPatchedApps(); final List<PatchedApplication> patchedApps = getPatchedApps();
List<PatchedApplication> unsavedApps = await getUnsavedApps(patchedApps); final List<PatchedApplication> unsavedApps =
await getUnsavedApps(patchedApps);
patchedApps.addAll(unsavedApps); patchedApps.addAll(unsavedApps);
List<PatchedApplication> toRemove = await getAppsToRemove(patchedApps); final List<PatchedApplication> toRemove =
await getAppsToRemove(patchedApps);
patchedApps.removeWhere((a) => toRemove.contains(a)); patchedApps.removeWhere((a) => toRemove.contains(a));
for (PatchedApplication app in patchedApps) { for (final PatchedApplication app in patchedApps) {
app.hasUpdates = app.hasUpdates =
await hasAppUpdates(app.originalPackageName, app.patchDate); await hasAppUpdates(app.originalPackageName, app.patchDate);
app.changelog = app.changelog =
await getAppChangelog(app.originalPackageName, app.patchDate); await getAppChangelog(app.originalPackageName, app.patchDate);
if (!app.hasUpdates) { if (!app.hasUpdates) {
String? currentInstalledVersion = final String? currentInstalledVersion =
(await DeviceApps.getApp(app.packageName))?.versionName; (await DeviceApps.getApp(app.packageName))?.versionName;
if (currentInstalledVersion != null) { if (currentInstalledVersion != null) {
String currentSavedVersion = app.version; final String currentSavedVersion = app.version;
int currentInstalledVersionInt = int.parse( final int currentInstalledVersionInt = int.parse(
currentInstalledVersion.replaceAll(RegExp('[^0-9]'), '')); currentInstalledVersion.replaceAll(RegExp('[^0-9]'), ''),
int currentSavedVersionInt = );
final int currentSavedVersionInt =
int.parse(currentSavedVersion.replaceAll(RegExp('[^0-9]'), '')); int.parse(currentSavedVersion.replaceAll(RegExp('[^0-9]'), ''));
if (currentInstalledVersionInt > currentSavedVersionInt) { if (currentInstalledVersionInt > currentSavedVersionInt) {
app.hasUpdates = true; app.hasUpdates = true;
@ -369,9 +372,9 @@ class ManagerAPI {
Future<bool> isAppUninstalled(PatchedApplication app) async { Future<bool> isAppUninstalled(PatchedApplication app) async {
bool existsRoot = false; bool existsRoot = false;
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName); final bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
if (app.isRooted) { if (app.isRooted) {
bool hasRootPermissions = await _rootAPI.hasRootPermissions(); final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) { if (hasRootPermissions) {
existsRoot = await _rootAPI.isAppInstalled(app.packageName); existsRoot = await _rootAPI.isAppInstalled(app.packageName);
} }
@ -381,7 +384,7 @@ class ManagerAPI {
} }
Future<bool> hasAppUpdates(String packageName, DateTime patchDate) async { Future<bool> hasAppUpdates(String packageName, DateTime patchDate) async {
List<String> commits = await _githubAPI.getCommits( final List<String> commits = await _githubAPI.getCommits(
packageName, packageName,
getPatchesRepo(), getPatchesRepo(),
patchDate, patchDate,
@ -390,7 +393,9 @@ class ManagerAPI {
} }
Future<List<String>> getAppChangelog( Future<List<String>> getAppChangelog(
String packageName, DateTime patchDate) async { String packageName,
DateTime patchDate,
) async {
List<String> newCommits = await _githubAPI.getCommits( List<String> newCommits = await _githubAPI.getCommits(
packageName, packageName,
getPatchesRepo(), getPatchesRepo(),
@ -418,36 +423,30 @@ class ManagerAPI {
Future<void> setSelectedPatches(String app, List<String> patches) async { Future<void> setSelectedPatches(String app, List<String> patches) async {
final File selectedPatchesFile = File(storedPatchesFile); final File selectedPatchesFile = File(storedPatchesFile);
Map<String, dynamic> patchesMap = await readSelectedPatchesFile(); final Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
if (patches.isEmpty) { if (patches.isEmpty) {
patchesMap.remove(app); patchesMap.remove(app);
} else { } else {
patchesMap[app] = patches; patchesMap[app] = patches;
} }
if (selectedPatchesFile.existsSync()) {
selectedPatchesFile.createSync(recursive: true);
}
selectedPatchesFile.writeAsString(jsonEncode(patchesMap)); selectedPatchesFile.writeAsString(jsonEncode(patchesMap));
} }
Future<List<String>> getSelectedPatches(String app) async { Future<List<String>> getSelectedPatches(String app) async {
Map<String, dynamic> patchesMap = await readSelectedPatchesFile(); final Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
if (patchesMap.isNotEmpty) { return List.from(patchesMap.putIfAbsent(app, () => List.empty()));
final List<String> patches =
List.from(patchesMap.putIfAbsent(app, () => List.empty()));
return patches;
}
return List.empty();
} }
Future<Map<String, dynamic>> readSelectedPatchesFile() async { Future<Map<String, dynamic>> readSelectedPatchesFile() async {
final File selectedPatchesFile = File(storedPatchesFile); final File selectedPatchesFile = File(storedPatchesFile);
if (selectedPatchesFile.existsSync()) { if (!selectedPatchesFile.existsSync()) {
String string = selectedPatchesFile.readAsStringSync(); return {};
if (string.trim().isEmpty) return {};
return json.decode(string);
} }
return {}; final String string = selectedPatchesFile.readAsStringSync();
if (string.trim().isEmpty) {
return {};
}
return jsonDecode(string);
} }
Future<void> resetLastSelectedPatches() async { Future<void> resetLastSelectedPatches() async {

View File

@ -1,7 +1,10 @@
import 'dart:io'; import 'dart:io';
import 'package:app_installer/app_installer.dart'; import 'package:app_installer/app_installer.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:cr_file_saver/file_saver.dart';
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@ -12,7 +15,6 @@ import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/root_api.dart'; import 'package:revanced_manager/services/root_api.dart';
import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:share_extend/share_extend.dart'; import 'package:share_extend/share_extend.dart';
import 'package:cr_file_saver/file_saver.dart';
@lazySingleton @lazySingleton
class PatcherAPI { class PatcherAPI {
@ -29,7 +31,7 @@ class PatcherAPI {
Future<void> initialize() async { Future<void> initialize() async {
await _loadPatches(); await _loadPatches();
Directory appCache = await getTemporaryDirectory(); final Directory appCache = await getTemporaryDirectory();
_dataDir = await getExternalStorageDirectory() ?? appCache; _dataDir = await getExternalStorageDirectory() ?? appCache;
_tmpDir = Directory('${appCache.path}/patcher'); _tmpDir = Directory('${appCache.path}/patcher');
_keyStoreFile = File('${_dataDir.path}/revanced-manager.keystore'); _keyStoreFile = File('${_dataDir.path}/revanced-manager.keystore');
@ -53,18 +55,21 @@ class PatcherAPI {
} }
} }
Future<List<ApplicationWithIcon>> getFilteredInstalledApps(bool showUniversalPatches) async { Future<List<ApplicationWithIcon>> getFilteredInstalledApps(
List<ApplicationWithIcon> filteredApps = []; bool showUniversalPatches,
bool? allAppsIncluded = ) async {
_patches.any((patch) => patch.compatiblePackages.isEmpty) && showUniversalPatches; final List<ApplicationWithIcon> filteredApps = [];
final bool allAppsIncluded =
_patches.any((patch) => patch.compatiblePackages.isEmpty) &&
showUniversalPatches;
if (allAppsIncluded) { if (allAppsIncluded) {
var allPackages = await DeviceApps.getInstalledApplications( final allPackages = await DeviceApps.getInstalledApplications(
includeAppIcons: true, includeAppIcons: true,
onlyAppsWithLaunchIntent: true, onlyAppsWithLaunchIntent: true,
); );
allPackages.forEach((pkg) async { for (final pkg in allPackages) {
if (!filteredApps.any((app) => app.packageName == pkg.packageName)) { if (!filteredApps.any((app) => app.packageName == pkg.packageName)) {
var appInfo = await DeviceApps.getApp( final appInfo = await DeviceApps.getApp(
pkg.packageName, pkg.packageName,
true, true,
) as ApplicationWithIcon?; ) as ApplicationWithIcon?;
@ -72,13 +77,13 @@ class PatcherAPI {
filteredApps.add(appInfo); filteredApps.add(appInfo);
} }
} }
}); }
} }
for (Patch patch in _patches) { for (final Patch patch in _patches) {
for (Package package in patch.compatiblePackages) { for (final Package package in patch.compatiblePackages) {
try { try {
if (!filteredApps.any((app) => app.packageName == package.name)) { if (!filteredApps.any((app) => app.packageName == package.name)) {
ApplicationWithIcon? app = await DeviceApps.getApp( final ApplicationWithIcon? app = await DeviceApps.getApp(
package.name, package.name,
true, true,
) as ApplicationWithIcon?; ) as ApplicationWithIcon?;
@ -97,12 +102,14 @@ class PatcherAPI {
List<Patch> getFilteredPatches(String packageName) { List<Patch> getFilteredPatches(String packageName) {
if (!filteredPatches.keys.contains(packageName)) { if (!filteredPatches.keys.contains(packageName)) {
List<Patch> patches = _patches final List<Patch> patches = _patches
.where((patch) => .where(
patch.compatiblePackages.isEmpty || (patch) =>
!patch.name.contains('settings') && patch.compatiblePackages.isEmpty ||
patch.compatiblePackages !patch.name.contains('settings') &&
.any((pack) => pack.name == packageName)) patch.compatiblePackages
.any((pack) => pack.name == packageName),
)
.toList(); .toList();
filteredPatches[packageName] = patches; filteredPatches[packageName] = patches;
} }
@ -155,7 +162,7 @@ class PatcherAPI {
String originalFilePath, String originalFilePath,
) async { ) async {
try { try {
bool hasRootPermissions = await _rootAPI.hasRootPermissions(); final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) { if (hasRootPermissions) {
originalFilePath = await _rootAPI.getOriginalFilePath( originalFilePath = await _rootAPI.getOriginalFilePath(
packageName, packageName,
@ -174,11 +181,11 @@ class PatcherAPI {
String originalFilePath, String originalFilePath,
List<Patch> selectedPatches, List<Patch> selectedPatches,
) async { ) async {
bool mergeIntegrations = await needsIntegrations(selectedPatches); final bool mergeIntegrations = await needsIntegrations(selectedPatches);
bool includeSettings = await needsSettingsPatch(selectedPatches); final bool includeSettings = await needsSettingsPatch(selectedPatches);
if (includeSettings) { if (includeSettings) {
try { try {
Patch? settingsPatch = _patches.firstWhereOrNull( final Patch? settingsPatch = _patches.firstWhereOrNull(
(patch) => (patch) =>
patch.name.contains('settings') && patch.name.contains('settings') &&
patch.compatiblePackages.any((pack) => pack.name == packageName), patch.compatiblePackages.any((pack) => pack.name == packageName),
@ -191,7 +198,7 @@ class PatcherAPI {
// ignore // ignore
} }
} }
File? patchBundleFile = await _managerAPI.downloadPatches(); final File? patchBundleFile = await _managerAPI.downloadPatches();
File? integrationsFile; File? integrationsFile;
if (mergeIntegrations) { if (mergeIntegrations) {
integrationsFile = await _managerAPI.downloadIntegrations(); integrationsFile = await _managerAPI.downloadIntegrations();
@ -199,11 +206,11 @@ class PatcherAPI {
if (patchBundleFile != null) { if (patchBundleFile != null) {
_dataDir.createSync(); _dataDir.createSync();
_tmpDir.createSync(); _tmpDir.createSync();
Directory workDir = _tmpDir.createTempSync('tmp-'); final Directory workDir = _tmpDir.createTempSync('tmp-');
File inputFile = File('${workDir.path}/base.apk'); final File inputFile = File('${workDir.path}/base.apk');
File patchedFile = File('${workDir.path}/patched.apk'); final File patchedFile = File('${workDir.path}/patched.apk');
_outFile = File('${workDir.path}/out.apk'); _outFile = File('${workDir.path}/out.apk');
Directory cacheDir = Directory('${workDir.path}/cache'); final Directory cacheDir = Directory('${workDir.path}/cache');
cacheDir.createSync(); cacheDir.createSync();
try { try {
await patcherChannel.invokeMethod( await patcherChannel.invokeMethod(
@ -225,7 +232,9 @@ class PatcherAPI {
}, },
); );
} on Exception catch (e, s) { } on Exception catch (e, s) {
print(e); if (kDebugMode) {
print(e);
}
throw await Sentry.captureException(e, stackTrace: s); throw await Sentry.captureException(e, stackTrace: s);
} }
} }
@ -235,7 +244,7 @@ class PatcherAPI {
if (_outFile != null) { if (_outFile != null) {
try { try {
if (patchedApp.isRooted) { if (patchedApp.isRooted) {
bool hasRootPermissions = await _rootAPI.hasRootPermissions(); final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) { if (hasRootPermissions) {
return _rootAPI.installApp( return _rootAPI.installApp(
patchedApp.packageName, patchedApp.packageName,
@ -258,17 +267,9 @@ class PatcherAPI {
void exportPatchedFile(String appName, String version) { void exportPatchedFile(String appName, String version) {
try { try {
if (_outFile != null) { if (_outFile != null) {
String newName = _getFileName(appName, version); final String newName = _getFileName(appName, version);
// This is temporary workaround to populate initial file name
// ref: https://github.com/Cleveroad/cr_file_saver/issues/7
int lastSeparator = _outFile!.path.lastIndexOf('/');
String newSourcePath =
_outFile!.path.substring(0, lastSeparator + 1) + newName;
_outFile!.copySync(newSourcePath);
CRFileSaver.saveFileWithDialog(SaveFileDialogParams( CRFileSaver.saveFileWithDialog(SaveFileDialogParams(
sourceFilePath: newSourcePath, destinationFileName: newName)); sourceFilePath: _outFile!.path, destinationFileName: newName,),);
} }
} on Exception catch (e, s) { } on Exception catch (e, s) {
Sentry.captureException(e, stackTrace: s); Sentry.captureException(e, stackTrace: s);
@ -278,11 +279,11 @@ class PatcherAPI {
void sharePatchedFile(String appName, String version) { void sharePatchedFile(String appName, String version) {
try { try {
if (_outFile != null) { if (_outFile != null) {
String newName = _getFileName(appName, version); final String newName = _getFileName(appName, version);
int lastSeparator = _outFile!.path.lastIndexOf('/'); final int lastSeparator = _outFile!.path.lastIndexOf('/');
String newPath = final String newPath =
_outFile!.path.substring(0, lastSeparator + 1) + newName; _outFile!.path.substring(0, lastSeparator + 1) + newName;
File shareFile = _outFile!.copySync(newPath); final File shareFile = _outFile!.copySync(newPath);
ShareExtend.share(shareFile.path, 'file'); ShareExtend.share(shareFile.path, 'file');
} }
} on Exception catch (e, s) { } on Exception catch (e, s) {
@ -291,34 +292,35 @@ class PatcherAPI {
} }
String _getFileName(String appName, String version) { String _getFileName(String appName, String version) {
String prefix = appName.toLowerCase().replaceAll(' ', '-'); final String prefix = appName.toLowerCase().replaceAll(' ', '-');
String newName = '$prefix-revanced_v$version.apk'; final String newName = '$prefix-revanced_v$version.apk';
return newName; return newName;
} }
Future<void> sharePatcherLog(String logs) async { Future<void> sharePatcherLog(String logs) async {
Directory appCache = await getTemporaryDirectory(); final Directory appCache = await getTemporaryDirectory();
Directory logDir = Directory('${appCache.path}/logs'); final Directory logDir = Directory('${appCache.path}/logs');
logDir.createSync(); logDir.createSync();
String dateTime = DateTime.now() final String dateTime = DateTime.now()
.toIso8601String() .toIso8601String()
.replaceAll('-', '') .replaceAll('-', '')
.replaceAll(':', '') .replaceAll(':', '')
.replaceAll('T', '') .replaceAll('T', '')
.replaceAll('.', ''); .replaceAll('.', '');
File log = File('${logDir.path}/revanced-manager_patcher_$dateTime.log'); final File log =
File('${logDir.path}/revanced-manager_patcher_$dateTime.log');
log.writeAsStringSync(logs); log.writeAsStringSync(logs);
ShareExtend.share(log.path, 'file'); ShareExtend.share(log.path, 'file');
} }
String getRecommendedVersion(String packageName) { String getRecommendedVersion(String packageName) {
Map<String, int> versions = {}; final Map<String, int> versions = {};
for (Patch patch in _patches) { for (final Patch patch in _patches) {
Package? package = patch.compatiblePackages.firstWhereOrNull( final Package? package = patch.compatiblePackages.firstWhereOrNull(
(pack) => pack.name == packageName, (pack) => pack.name == packageName,
); );
if (package != null) { if (package != null) {
for (String version in package.versions) { for (final String version in package.versions) {
versions.update( versions.update(
version, version,
(value) => versions[version]! + 1, (value) => versions[version]! + 1,
@ -328,7 +330,7 @@ class PatcherAPI {
} }
} }
if (versions.isNotEmpty) { if (versions.isNotEmpty) {
var entries = versions.entries.toList() final entries = versions.entries.toList()
..sort((a, b) => a.value.compareTo(b.value)); ..sort((a, b) => a.value.compareTo(b.value));
versions versions
..clear() ..clear()

View File

@ -1,15 +1,17 @@
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:native_dio_client/native_dio_client.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio_http_cache_lts/dio_http_cache_lts.dart'; import 'package:dio_http_cache_lts/dio_http_cache_lts.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:native_dio_client/native_dio_client.dart';
import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/utils/check_for_gms.dart'; import 'package:revanced_manager/utils/check_for_gms.dart';
import 'package:timeago/timeago.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_dio/sentry_dio.dart'; import 'package:sentry_dio/sentry_dio.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:timeago/timeago.dart';
@lazySingleton @lazySingleton
class RevancedAPI { class RevancedAPI {
@ -22,19 +24,22 @@ class RevancedAPI {
Future<void> initialize(String apiUrl) async { Future<void> initialize(String apiUrl) async {
try { try {
bool isGMSInstalled = await checkForGMS(); final bool isGMSInstalled = await checkForGMS();
if (!isGMSInstalled) { if (!isGMSInstalled) {
_dio = Dio(BaseOptions( _dio = Dio(
baseUrl: apiUrl, BaseOptions(
)); baseUrl: apiUrl,
print('ReVanced API: Using default engine + $isGMSInstalled'); ),
);
log('ReVanced API: Using default engine + $isGMSInstalled');
} else { } else {
_dio = Dio(BaseOptions( _dio = Dio(
baseUrl: apiUrl, BaseOptions(
)) baseUrl: apiUrl,
..httpClientAdapter = NativeAdapter(); ),
print('ReVanced API: Using CronetEngine + $isGMSInstalled'); )..httpClientAdapter = NativeAdapter();
log('ReVanced API: Using CronetEngine + $isGMSInstalled');
} }
_dio.interceptors.add(_dioCacheManager.interceptor); _dio.interceptors.add(_dioCacheManager.interceptor);
_dio.addSentry( _dio.addSentry(
@ -54,12 +59,12 @@ class RevancedAPI {
} }
Future<Map<String, List<dynamic>>> getContributors() async { Future<Map<String, List<dynamic>>> getContributors() async {
Map<String, List<dynamic>> contributors = {}; final Map<String, List<dynamic>> contributors = {};
try { try {
var response = await _dio.get('/contributors', options: _cacheOptions); final response = await _dio.get('/contributors', options: _cacheOptions);
List<dynamic> repositories = response.data['repositories']; final List<dynamic> repositories = response.data['repositories'];
for (Map<String, dynamic> repo in repositories) { for (final Map<String, dynamic> repo in repositories) {
String name = repo['name']; final String name = repo['name'];
contributors[name] = repo['contributors']; contributors[name] = repo['contributors'];
} }
} on Exception catch (e, s) { } on Exception catch (e, s) {
@ -71,8 +76,8 @@ class RevancedAPI {
Future<List<Patch>> getPatches() async { Future<List<Patch>> getPatches() async {
try { try {
var response = await _dio.get('/patches', options: _cacheOptions); final response = await _dio.get('/patches', options: _cacheOptions);
List<dynamic> patches = response.data; final List<dynamic> patches = response.data;
return patches.map((patch) => Patch.fromJson(patch)).toList(); return patches.map((patch) => Patch.fromJson(patch)).toList();
} on Exception catch (e, s) { } on Exception catch (e, s) {
await Sentry.captureException(e, stackTrace: s); await Sentry.captureException(e, stackTrace: s);
@ -85,8 +90,8 @@ class RevancedAPI {
String repoName, String repoName,
) async { ) async {
try { try {
var response = await _dio.get('/tools', options: _cacheOptions); final response = await _dio.get('/tools', options: _cacheOptions);
List<dynamic> tools = response.data['tools']; final List<dynamic> tools = response.data['tools'];
return tools.firstWhereOrNull( return tools.firstWhereOrNull(
(t) => (t) =>
t['repository'] == repoName && t['repository'] == repoName &&
@ -103,7 +108,7 @@ class RevancedAPI {
String repoName, String repoName,
) async { ) async {
try { try {
Map<String, dynamic>? release = await _getLatestRelease( final Map<String, dynamic>? release = await _getLatestRelease(
extension, extension,
repoName, repoName,
); );
@ -119,12 +124,12 @@ class RevancedAPI {
Future<File?> getLatestReleaseFile(String extension, String repoName) async { Future<File?> getLatestReleaseFile(String extension, String repoName) async {
try { try {
Map<String, dynamic>? release = await _getLatestRelease( final Map<String, dynamic>? release = await _getLatestRelease(
extension, extension,
repoName, repoName,
); );
if (release != null) { if (release != null) {
String url = release['browser_download_url']; final String url = release['browser_download_url'];
return await DefaultCacheManager().getSingleFile(url); return await DefaultCacheManager().getSingleFile(url);
} }
} on Exception catch (e, s) { } on Exception catch (e, s) {
@ -139,12 +144,13 @@ class RevancedAPI {
String repoName, String repoName,
) async { ) async {
try { try {
Map<String, dynamic>? release = await _getLatestRelease( final Map<String, dynamic>? release = await _getLatestRelease(
extension, extension,
repoName, repoName,
); );
if (release != null) { if (release != null) {
DateTime timestamp = DateTime.parse(release['timestamp'] as String); final DateTime timestamp =
DateTime.parse(release['timestamp'] as String);
return format(timestamp, locale: 'en_short'); return format(timestamp, locale: 'en_short');
} }
} on Exception catch (e, s) { } on Exception catch (e, s) {

View File

@ -8,7 +8,7 @@ class RootAPI {
Future<bool> isRooted() async { Future<bool> isRooted() async {
try { try {
bool? isRooted = await Root.isRootAvailable(); final bool? isRooted = await Root.isRootAvailable();
return isRooted != null && isRooted; return isRooted != null && isRooted;
} on Exception catch (e, s) { } on Exception catch (e, s) {
await Sentry.captureException(e, stackTrace: s); await Sentry.captureException(e, stackTrace: s);
@ -70,11 +70,11 @@ class RootAPI {
Future<List<String>> getInstalledApps() async { Future<List<String>> getInstalledApps() async {
try { try {
String? res = await Root.exec( final String? res = await Root.exec(
cmd: 'ls "$_managerDirPath"', cmd: 'ls "$_managerDirPath"',
); );
if (res != null) { if (res != null) {
List<String> apps = res.split('\n'); final List<String> apps = res.split('\n');
apps.removeWhere((pack) => pack.isEmpty); apps.removeWhere((pack) => pack.isEmpty);
return apps.map((pack) => pack.trim()).toList(); return apps.map((pack) => pack.trim()).toList();
} }
@ -132,12 +132,12 @@ class RootAPI {
} }
Future<void> installServiceDScript(String packageName) async { Future<void> installServiceDScript(String packageName) async {
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=$_managerDirPath/$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'
'[ ! -z \$stock_path ] && mount -o bind \$base_path \$stock_path'; r'[ ! -z $stock_path ] && mount -o bind $base_path $stock_path';
String scriptFilePath = '$_serviceDDirPath/$packageName.sh'; final String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
await Root.exec( await Root.exec(
cmd: 'echo \'$content\' > "$scriptFilePath"', cmd: 'echo \'$content\' > "$scriptFilePath"',
); );
@ -145,10 +145,10 @@ class RootAPI {
} }
Future<void> installPostFsDataScript(String packageName) async { Future<void> installPostFsDataScript(String packageName) async {
String content = '#!/system/bin/sh\n' final String content = '#!/system/bin/sh\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'
'[ ! -z \$stock_path ] && umount -l \$stock_path'; r'[ ! -z $stock_path ] && umount -l $stock_path';
String scriptFilePath = '$_postFsDataDirPath/$packageName.sh'; final String scriptFilePath = '$_postFsDataDirPath/$packageName.sh';
await Root.exec( await Root.exec(
cmd: 'echo \'$content\' > "$scriptFilePath"', cmd: 'echo \'$content\' > "$scriptFilePath"',
); );
@ -156,7 +156,7 @@ class RootAPI {
} }
Future<void> installApk(String packageName, String patchedFilePath) async { Future<void> installApk(String packageName, String patchedFilePath) async {
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk'; final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
await Root.exec( await Root.exec(
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"', cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
); );
@ -169,7 +169,7 @@ class RootAPI {
} }
Future<void> mountApk(String packageName, String originalFilePath) async { Future<void> mountApk(String packageName, String originalFilePath) async {
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk'; final String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
await Root.exec( await Root.exec(
cmd: 'am force-stop "$packageName"', cmd: 'am force-stop "$packageName"',
); );
@ -182,7 +182,7 @@ class RootAPI {
} }
Future<bool> isMounted(String packageName) async { Future<bool> isMounted(String packageName) async {
String? res = await Root.exec( final String? res = await Root.exec(
cmd: 'cat /proc/mounts | grep $packageName', cmd: 'cat /proc/mounts | grep $packageName',
); );
return res != null && res.isNotEmpty; return res != null && res.isNotEmpty;
@ -192,7 +192,7 @@ class RootAPI {
String packageName, String packageName,
String originalFilePath, String originalFilePath,
) async { ) async {
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'; originalFilePath = '$_managerDirPath/$packageName/original.apk';
await setPermissions( await setPermissions(
@ -209,7 +209,8 @@ class RootAPI {
String packageName, String packageName,
String originalFilePath, String originalFilePath,
) async { ) async {
String originalRootPath = '$_managerDirPath/$packageName/original.apk'; final String originalRootPath =
'$_managerDirPath/$packageName/original.apk';
await Root.exec( await Root.exec(
cmd: 'mkdir -p "$_managerDirPath/$packageName"', cmd: 'mkdir -p "$_managerDirPath/$packageName"',
); );

View File

@ -3,7 +3,6 @@ import 'package:google_fonts/google_fonts.dart';
var lightCustomColorScheme = ColorScheme.fromSeed( var lightCustomColorScheme = ColorScheme.fromSeed(
seedColor: Colors.blue, seedColor: Colors.blue,
brightness: Brightness.light,
primary: const Color(0xff1B73E8), primary: const Color(0xff1B73E8),
); );

View File

@ -7,63 +7,62 @@ import 'package:revanced_manager/theme.dart';
import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_services/stacked_services.dart';
class DynamicThemeBuilder extends StatelessWidget { class DynamicThemeBuilder extends StatelessWidget {
final String title;
final Widget home;
final Iterable<LocalizationsDelegate> localizationsDelegates;
const DynamicThemeBuilder({ const DynamicThemeBuilder({
Key? key, Key? key,
required this.title, required this.title,
required this.home, required this.home,
required this.localizationsDelegates, required this.localizationsDelegates,
}) : super(key: key); }) : super(key: key);
final String title;
final Widget home;
final Iterable<LocalizationsDelegate> localizationsDelegates;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DynamicColorBuilder( return DynamicColorBuilder(
builder: (lightColorScheme, darkColorScheme) { builder: (lightColorScheme, darkColorScheme) {
ThemeData lightDynamicTheme = ThemeData( final ThemeData lightDynamicTheme = ThemeData(
useMaterial3: true, useMaterial3: true,
canvasColor: lightColorScheme?.background, canvasColor: lightColorScheme?.surface,
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
backgroundColor: lightColorScheme?.background, backgroundColor: lightColorScheme?.surface,
indicatorColor: lightColorScheme?.primary.withAlpha(150), indicatorColor: lightColorScheme?.secondaryContainer,
labelTextStyle: MaterialStateProperty.all( labelTextStyle: MaterialStateProperty.all(
GoogleFonts.roboto( GoogleFonts.roboto(
color: lightColorScheme?.secondary, color: lightColorScheme?.onSurface,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
iconTheme: MaterialStateProperty.all( iconTheme: MaterialStateProperty.all(
IconThemeData( IconThemeData(
color: lightColorScheme?.secondary, color: lightColorScheme?.onSecondaryContainer,
), ),
), ),
), ),
scaffoldBackgroundColor: lightColorScheme?.background, scaffoldBackgroundColor: lightColorScheme?.surface,
colorScheme: lightColorScheme?.harmonized(), colorScheme: lightColorScheme?.harmonized(),
toggleableActiveColor: lightColorScheme?.primary, toggleableActiveColor: lightColorScheme?.primary,
textTheme: GoogleFonts.robotoTextTheme(ThemeData.light().textTheme), textTheme: GoogleFonts.robotoTextTheme(ThemeData.light().textTheme),
); );
ThemeData darkDynamicTheme = ThemeData( final ThemeData darkDynamicTheme = ThemeData(
useMaterial3: true, useMaterial3: true,
canvasColor: darkColorScheme?.background, canvasColor: darkColorScheme?.surface,
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
backgroundColor: darkColorScheme?.background, backgroundColor: darkColorScheme?.surface,
indicatorColor: darkColorScheme?.primary.withOpacity(0.4), indicatorColor: darkColorScheme?.secondaryContainer,
labelTextStyle: MaterialStateProperty.all( labelTextStyle: MaterialStateProperty.all(
GoogleFonts.roboto( GoogleFonts.roboto(
color: darkColorScheme?.secondary, color: darkColorScheme?.onSurface,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
iconTheme: MaterialStateProperty.all( iconTheme: MaterialStateProperty.all(
IconThemeData( IconThemeData(
color: darkColorScheme?.secondary, color: darkColorScheme?.onSecondaryContainer,
), ),
), ),
), ),
scaffoldBackgroundColor: darkColorScheme?.background, scaffoldBackgroundColor: darkColorScheme?.surface,
colorScheme: darkColorScheme?.harmonized(), colorScheme: darkColorScheme?.harmonized(),
toggleableActiveColor: darkColorScheme?.primary, toggleableActiveColor: darkColorScheme?.primary,
textTheme: GoogleFonts.robotoTextTheme(ThemeData.dark().textTheme), textTheme: GoogleFonts.robotoTextTheme(ThemeData.dark().textTheme),
@ -91,4 +90,4 @@ class DynamicThemeBuilder extends StatelessWidget {
}, },
); );
} }
} }

View File

@ -1,10 +1,10 @@
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/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart'; import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart';
import 'package:revanced_manager/ui/widgets/shared/search_bar.dart'; import 'package:revanced_manager/ui/widgets/shared/search_bar.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart';
import 'package:stacked/stacked.dart' hide SkeletonLoader; import 'package:stacked/stacked.dart' hide SkeletonLoader;
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
class AppSelectorView extends StatefulWidget { class AppSelectorView extends StatefulWidget {
const AppSelectorView({Key? key}) : super(key: key); const AppSelectorView({Key? key}) : super(key: key);
@ -19,7 +19,7 @@ class _AppSelectorViewState extends State<AppSelectorView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ViewModelBuilder<AppSelectorViewModel>.reactive( return ViewModelBuilder<AppSelectorViewModel>.reactive(
onModelReady: (model) => model.initialize(), onViewModelReady: (model) => model.initialize(),
viewModelBuilder: () => AppSelectorViewModel(), viewModelBuilder: () => AppSelectorViewModel(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
@ -36,7 +36,6 @@ class _AppSelectorViewState extends State<AppSelectorView> {
SliverAppBar( SliverAppBar(
pinned: true, pinned: true,
floating: true, floating: true,
snap: false,
title: I18nText( title: I18nText(
'appSelectorView.viewTitle', 'appSelectorView.viewTitle',
child: Text( child: Text(
@ -61,7 +60,6 @@ class _AppSelectorViewState extends State<AppSelectorView> {
horizontal: 12.0, horizontal: 12.0,
), ),
child: SearchBar( child: SearchBar(
showSelectIcon: false,
hintText: FlutterI18n.translate( hintText: FlutterI18n.translate(
context, context,
'appSelectorView.searchBarHint', 'appSelectorView.searchBarHint',
@ -88,17 +86,19 @@ class _AppSelectorViewState extends State<AppSelectorView> {
child: Column( child: Column(
children: model children: model
.getFilteredApps(_query) .getFilteredApps(_query)
.map((app) => InstalledAppItem( .map(
name: app.appName, (app) => InstalledAppItem(
pkgName: app.packageName, name: app.appName,
icon: app.icon, pkgName: app.packageName,
patchesCount: icon: app.icon,
model.patchesCount(app.packageName), patchesCount:
onTap: () { model.patchesCount(app.packageName),
model.selectApp(app); onTap: () {
Navigator.of(context).pop(); model.selectApp(app);
}, Navigator.of(context).pop();
)) },
),
)
.toList(), .toList(),
), ),
), ),

View File

@ -1,15 +1,16 @@
import 'dart:io'; import 'dart:io';
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/services/toast.dart'; import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import '../../../services/manager_api.dart';
class AppSelectorViewModel extends BaseViewModel { class AppSelectorViewModel extends BaseViewModel {
final PatcherAPI _patcherAPI = locator<PatcherAPI>(); final PatcherAPI _patcherAPI = locator<PatcherAPI>();
@ -22,16 +23,21 @@ class AppSelectorViewModel extends BaseViewModel {
} }
Future<void> initialize() async { Future<void> initialize() async {
apps.addAll(await _patcherAPI.getFilteredInstalledApps(_managerAPI.areUniversalPatchesEnabled())); apps.addAll(
apps.sort(((a, b) => _patcherAPI await _patcherAPI
.getFilteredPatches(b.packageName) .getFilteredInstalledApps(_managerAPI.areUniversalPatchesEnabled()),
.length );
.compareTo(_patcherAPI.getFilteredPatches(a.packageName).length))); apps.sort(
(a, b) => _patcherAPI
.getFilteredPatches(b.packageName)
.length
.compareTo(_patcherAPI.getFilteredPatches(a.packageName).length),
);
noApps = apps.isEmpty; noApps = apps.isEmpty;
notifyListeners(); notifyListeners();
} }
void selectApp(ApplicationWithIcon application) async { Future<void> selectApp(ApplicationWithIcon application) async {
locator<PatcherViewModel>().selectedApp = PatchedApplication( locator<PatcherViewModel>().selectedApp = PatchedApplication(
name: application.appName, name: application.appName,
packageName: application.packageName, packageName: application.packageName,
@ -42,27 +48,28 @@ class AppSelectorViewModel extends BaseViewModel {
patchDate: DateTime.now(), patchDate: DateTime.now(),
); );
locator<PatcherViewModel>().loadLastSelectedPatches(); locator<PatcherViewModel>().loadLastSelectedPatches();
locator<PatcherViewModel>().notifyListeners();
} }
Future<void> selectAppFromStorage(BuildContext context) async { Future<void> selectAppFromStorage(BuildContext context) async {
try { try {
FilePickerResult? result = await FilePicker.platform.pickFiles( final FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom, type: FileType.custom,
allowedExtensions: ['apk'], allowedExtensions: ['apk'],
); );
if (result != null && result.files.single.path != null) { if (result != null && result.files.single.path != null) {
File apkFile = File(result.files.single.path!); final File apkFile = File(result.files.single.path!);
List<String> pathSplit = result.files.single.path!.split("/"); final List<String> pathSplit = result.files.single.path!.split('/');
pathSplit.removeLast(); pathSplit.removeLast();
Directory filePickerCacheDir = Directory(pathSplit.join("/")); final Directory filePickerCacheDir = Directory(pathSplit.join('/'));
Iterable<File> deletableFiles = final Iterable<File> deletableFiles =
(await filePickerCacheDir.list().toList()).whereType<File>(); (await filePickerCacheDir.list().toList()).whereType<File>();
for (var file in deletableFiles) { for (final file in deletableFiles) {
if (file.path != apkFile.path && file.path.endsWith(".apk")) if (file.path != apkFile.path && file.path.endsWith('.apk')) {
file.delete(); file.delete();
}
} }
ApplicationWithIcon? application = await DeviceApps.getAppFromStorage( final ApplicationWithIcon? application =
await DeviceApps.getAppFromStorage(
apkFile.path, apkFile.path,
true, true,
) as ApplicationWithIcon?; ) as ApplicationWithIcon?;
@ -78,7 +85,6 @@ class AppSelectorViewModel extends BaseViewModel {
isFromStorage: true, isFromStorage: true,
); );
locator<PatcherViewModel>().loadLastSelectedPatches(); locator<PatcherViewModel>().loadLastSelectedPatches();
locator<PatcherViewModel>().notifyListeners();
} }
} }
} on Exception catch (e, s) { } on Exception catch (e, s) {
@ -89,10 +95,12 @@ class AppSelectorViewModel extends BaseViewModel {
List<ApplicationWithIcon> getFilteredApps(String query) { List<ApplicationWithIcon> getFilteredApps(String query) {
return apps return apps
.where((app) => .where(
query.isEmpty || (app) =>
query.length < 2 || query.isEmpty ||
app.appName.toLowerCase().contains(query.toLowerCase())) query.length < 2 ||
app.appName.toLowerCase().contains(query.toLowerCase()),
)
.toList(); .toList();
} }
} }

View File

@ -13,7 +13,7 @@ class ContributorsView extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ViewModelBuilder<ContributorsViewModel>.reactive( return ViewModelBuilder<ContributorsViewModel>.reactive(
viewModelBuilder: () => ContributorsViewModel(), viewModelBuilder: () => ContributorsViewModel(),
onModelReady: (model) => model.getContributors(), onViewModelReady: (model) => model.getContributors(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
body: CustomScrollView( body: CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[

View File

@ -11,7 +11,7 @@ class ContributorsViewModel extends BaseViewModel {
List<dynamic> managerContributors = []; List<dynamic> managerContributors = [];
Future<void> getContributors() async { Future<void> getContributors() async {
Map<String, List<dynamic>> contributors = final Map<String, List<dynamic>> contributors =
await _managerAPI.getContributors(); await _managerAPI.getContributors();
patcherContributors = contributors[_managerAPI.defaultPatcherRepo] ?? []; patcherContributors = contributors[_managerAPI.defaultPatcherRepo] ?? [];
patchesContributors = contributors[_managerAPI.getPatchesRepo()] ?? []; patchesContributors = contributors[_managerAPI.getPatchesRepo()] ?? [];

View File

@ -1,7 +1,7 @@
import 'package:animations/animations.dart';
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:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:animations/animations.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart'; import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/homeView/available_updates_card.dart'; import 'package:revanced_manager/ui/widgets/homeView/available_updates_card.dart';
@ -18,7 +18,7 @@ class HomeView extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ViewModelBuilder<HomeViewModel>.reactive( return ViewModelBuilder<HomeViewModel>.reactive(
disposeViewModel: false, disposeViewModel: false,
onModelReady: (model) => model.initialize(context), onViewModelReady: (model) => model.initialize(context),
viewModelBuilder: () => locator<HomeViewModel>(), viewModelBuilder: () => locator<HomeViewModel>(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
body: RefreshIndicator( body: RefreshIndicator(
@ -48,7 +48,7 @@ class HomeView extends StatelessWidget {
'homeView.updatesSubtitle', 'homeView.updatesSubtitle',
child: Text( child: Text(
'', '',
style: Theme.of(context).textTheme.headline6!, style: Theme.of(context).textTheme.headline6,
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
@ -61,7 +61,7 @@ class HomeView extends StatelessWidget {
'homeView.patchedSubtitle', 'homeView.patchedSubtitle',
child: Text( child: Text(
'', '',
style: Theme.of(context).textTheme.headline6!, style: Theme.of(context).textTheme.headline6,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),

View File

@ -1,5 +1,6 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
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:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
@ -10,9 +11,9 @@ import 'package:injectable/injectable.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/app/app.router.dart'; import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/toast.dart'; import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
@ -47,7 +48,7 @@ class HomeViewModel extends BaseViewModel {
.resolvePlatformSpecificImplementation< .resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>() AndroidFlutterLocalNotificationsPlugin>()
?.requestPermission(); ?.requestPermission();
bool isConnected = await Connectivity().checkConnection(); final bool isConnected = await Connectivity().checkConnection();
if (!isConnected) { if (!isConnected) {
_toast.showBottom('homeView.noConnection'); _toast.showBottom('homeView.noConnection');
} }
@ -67,7 +68,7 @@ class HomeViewModel extends BaseViewModel {
notifyListeners(); notifyListeners();
} }
void navigateToPatcher(PatchedApplication app) async { Future<void> navigateToPatcher(PatchedApplication app) async {
locator<PatcherViewModel>().selectedApp = app; locator<PatcherViewModel>().selectedApp = app;
locator<PatcherViewModel>().selectedPatches = locator<PatcherViewModel>().selectedPatches =
await _patcherAPI.getAppliedPatches(app.appliedPatches); await _patcherAPI.getAppliedPatches(app.appliedPatches);
@ -76,10 +77,7 @@ class HomeViewModel extends BaseViewModel {
} }
void _getPatchedApps() { void _getPatchedApps() {
patchedInstalledApps = _managerAPI patchedInstalledApps = _managerAPI.getPatchedApps().toList();
.getPatchedApps()
.where((app) => app.hasUpdates == false)
.toList();
patchedUpdatableApps = _managerAPI patchedUpdatableApps = _managerAPI
.getPatchedApps() .getPatchedApps()
.where((app) => app.hasUpdates == true) .where((app) => app.hasUpdates == true)
@ -88,13 +86,13 @@ class HomeViewModel extends BaseViewModel {
} }
Future<bool> hasManagerUpdates() async { Future<bool> hasManagerUpdates() async {
String? latestVersion = await _managerAPI.getLatestManagerVersion(); final String? latestVersion = await _managerAPI.getLatestManagerVersion();
String currentVersion = await _managerAPI.getCurrentManagerVersion(); final String currentVersion = await _managerAPI.getCurrentManagerVersion();
if (latestVersion != null) { if (latestVersion != null) {
try { try {
int latestVersionInt = final int latestVersionInt =
int.parse(latestVersion.replaceAll(RegExp('[^0-9]'), '')); int.parse(latestVersion.replaceAll(RegExp('[^0-9]'), ''));
int currentVersionInt = final int currentVersionInt =
int.parse(currentVersion.replaceAll(RegExp('[^0-9]'), '')); int.parse(currentVersion.replaceAll(RegExp('[^0-9]'), ''));
return latestVersionInt > currentVersionInt; return latestVersionInt > currentVersionInt;
} on Exception catch (e, s) { } on Exception catch (e, s) {
@ -108,7 +106,7 @@ class HomeViewModel extends BaseViewModel {
Future<void> updateManager(BuildContext context) async { Future<void> updateManager(BuildContext context) async {
try { try {
_toast.showBottom('homeView.downloadingMessage'); _toast.showBottom('homeView.downloadingMessage');
File? managerApk = await _managerAPI.downloadManager(); final File? managerApk = await _managerAPI.downloadManager();
if (managerApk != null) { if (managerApk != null) {
await flutterLocalNotificationsPlugin.zonedSchedule( await flutterLocalNotificationsPlugin.zonedSchedule(
0, 0,

View File

@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart'; import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:revanced_manager/ui/widgets/installerView/gradient_progress_indicator.dart'; import 'package:revanced_manager/ui/widgets/installerView/gradient_progress_indicator.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_popup_menu.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_popup_menu.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
@ -15,7 +15,7 @@ class InstallerView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ViewModelBuilder<InstallerViewModel>.reactive( return ViewModelBuilder<InstallerViewModel>.reactive(
onModelReady: (model) => model.initialize(context), onViewModelReady: (model) => model.initialize(context),
viewModelBuilder: () => InstallerViewModel(), viewModelBuilder: () => InstallerViewModel(),
builder: (context, model, child) => WillPopScope( builder: (context, model, child) => WillPopScope(
child: SafeArea( child: SafeArea(
@ -48,15 +48,15 @@ class InstallerView extends StatelessWidget {
), ),
), ),
), ),
1: I18nText( 1: I18nText(
'installerView.exportApkMenuOption', 'installerView.exportApkMenuOption',
child: const Text( child: const Text(
'', '',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
),
), ),
), ),
),
2: I18nText( 2: I18nText(
'installerView.shareLogMenuOption', 'installerView.shareLogMenuOption',
child: const Text( child: const Text(
@ -71,9 +71,9 @@ class InstallerView extends StatelessWidget {
), ),
], ],
bottom: PreferredSize( bottom: PreferredSize(
preferredSize: const Size(double.infinity, 1.0), preferredSize: const Size(double.infinity, 1.0),
child: child: GradientProgressIndicator(progress: model.progress),
GradientProgressIndicator(progress: model.progress!)), ),
), ),
SliverPadding( SliverPadding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),

View File

@ -51,10 +51,8 @@ class InstallerViewModel extends BaseViewModel {
context, context,
'installerView.notificationText', 'installerView.notificationText',
), ),
notificationImportance: AndroidNotificationImportance.Default,
notificationIcon: const AndroidResource( notificationIcon: const AndroidResource(
name: 'ic_notification', name: 'ic_notification',
defType: 'drawable',
), ),
), ),
).then((value) => FlutterBackground.enableBackgroundExecution()); ).then((value) => FlutterBackground.enableBackgroundExecution());
@ -73,10 +71,10 @@ class InstallerViewModel extends BaseViewModel {
switch (call.method) { switch (call.method) {
case 'update': case 'update':
if (call.arguments != null) { if (call.arguments != null) {
Map<dynamic, dynamic> arguments = call.arguments; final Map<dynamic, dynamic> arguments = call.arguments;
double progress = arguments['progress']; final double progress = arguments['progress'];
String header = arguments['header']; final String header = arguments['header'];
String log = arguments['log']; final String log = arguments['log'];
update(progress, header, log); update(progress, header, log);
} }
break; break;
@ -159,13 +157,14 @@ class InstallerViewModel extends BaseViewModel {
} }
} }
void installResult(BuildContext context, bool installAsRoot) async { Future<void> installResult(BuildContext context, bool installAsRoot) async {
try { try {
_app.isRooted = installAsRoot; _app.isRooted = installAsRoot;
bool hasMicroG = _patches.any((p) => p.name.endsWith('microg-support')); final bool hasMicroG =
bool rootMicroG = installAsRoot && hasMicroG; _patches.any((p) => p.name.endsWith('microg-support'));
bool rootFromStorage = installAsRoot && _app.isFromStorage; final bool rootMicroG = installAsRoot && hasMicroG;
bool ytWithoutRootMicroG = final bool rootFromStorage = installAsRoot && _app.isFromStorage;
final bool ytWithoutRootMicroG =
!installAsRoot && !hasMicroG && _app.packageName.contains('youtube'); !installAsRoot && !hasMicroG && _app.packageName.contains('youtube');
if (rootMicroG || rootFromStorage || ytWithoutRootMicroG) { if (rootMicroG || rootFromStorage || ytWithoutRootMicroG) {
return showDialog( return showDialog(

View File

@ -11,7 +11,7 @@ class NavigationView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ViewModelBuilder<NavigationViewModel>.reactive( return ViewModelBuilder<NavigationViewModel>.reactive(
onModelReady: (model) => model.initialize(context), onViewModelReady: (model) => model.initialize(context),
viewModelBuilder: () => locator<NavigationViewModel>(), viewModelBuilder: () => locator<NavigationViewModel>(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
body: PageTransitionSwitcher( body: PageTransitionSwitcher(

View File

@ -15,9 +15,9 @@ import 'package:stacked/stacked.dart';
@lazySingleton @lazySingleton
class NavigationViewModel extends IndexTrackingViewModel { class NavigationViewModel extends IndexTrackingViewModel {
void initialize(BuildContext context) async { Future<void> initialize(BuildContext context) async {
locator<Toast>().initialize(context); locator<Toast>().initialize(context);
SharedPreferences prefs = await SharedPreferences.getInstance(); final SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getBool('permissionsRequested') == null) { if (prefs.getBool('permissionsRequested') == null) {
await prefs.setBool('permissionsRequested', true); await prefs.setBool('permissionsRequested', true);
RootAPI().hasRootPermissions().then( RootAPI().hasRootPermissions().then(
@ -27,7 +27,7 @@ class NavigationViewModel extends IndexTrackingViewModel {
); );
} }
if (prefs.getBool('useDarkTheme') == null) { if (prefs.getBool('useDarkTheme') == null) {
bool isDark = final bool isDark =
MediaQuery.of(context).platformBrightness != Brightness.light; MediaQuery.of(context).platformBrightness != Brightness.light;
await prefs.setBool('useDarkTheme', isDark); await prefs.setBool('useDarkTheme', isDark);
await DynamicTheme.of(context)!.setTheme(isDark ? 1 : 0); await DynamicTheme.of(context)!.setTheme(isDark ? 1 : 0);

View File

@ -40,18 +40,18 @@ class PatcherViewModel extends BaseViewModel {
} }
Future<bool> isValidPatchConfig() async { Future<bool> isValidPatchConfig() async {
bool needsResourcePatching = await _patcherAPI.needsResourcePatching( final bool needsResourcePatching = await _patcherAPI.needsResourcePatching(
selectedPatches, selectedPatches,
); );
if (needsResourcePatching && selectedApp != null) { if (needsResourcePatching && selectedApp != null) {
bool isSplit = await _managerAPI.isSplitApk(selectedApp!); final bool isSplit = await _managerAPI.isSplitApk(selectedApp!);
return !isSplit; return !isSplit;
} }
return true; return true;
} }
Future<void> showPatchConfirmationDialog(BuildContext context) async { Future<void> showPatchConfirmationDialog(BuildContext context) async {
bool isValid = await isValidPatchConfig(); final bool isValid = await isValidPatchConfig();
if (isValid) { if (isValid) {
navigateToInstaller(); navigateToInstaller();
} else { } else {
@ -110,12 +110,11 @@ class PatcherViewModel extends BaseViewModel {
Future<void> loadLastSelectedPatches() async { Future<void> loadLastSelectedPatches() async {
this.selectedPatches.clear(); this.selectedPatches.clear();
List<String> selectedPatches = final List<String> selectedPatches =
await _managerAPI.getSelectedPatches(selectedApp!.originalPackageName); await _managerAPI.getSelectedPatches(selectedApp!.originalPackageName);
List<Patch> patches = final List<Patch> patches =
_patcherAPI.getFilteredPatches(selectedApp!.originalPackageName); _patcherAPI.getFilteredPatches(selectedApp!.originalPackageName);
this this.selectedPatches
.selectedPatches
.addAll(patches.where((patch) => selectedPatches.contains(patch.name))); .addAll(patches.where((patch) => selectedPatches.contains(patch.name)));
notifyListeners(); notifyListeners();
} }

View File

@ -20,7 +20,7 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ViewModelBuilder<PatchesSelectorViewModel>.reactive( return ViewModelBuilder<PatchesSelectorViewModel>.reactive(
onModelReady: (model) => model.initialize(), onViewModelReady: (model) => model.initialize(),
viewModelBuilder: () => PatchesSelectorViewModel(), viewModelBuilder: () => PatchesSelectorViewModel(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
@ -45,7 +45,6 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
SliverAppBar( SliverAppBar(
pinned: true, pinned: true,
floating: true, floating: true,
snap: false,
title: I18nText( title: I18nText(
'patchesSelectorView.viewTitle', 'patchesSelectorView.viewTitle',
child: Text( child: Text(
@ -103,7 +102,6 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
horizontal: 12.0, horizontal: 12.0,
), ),
child: SearchBar( child: SearchBar(
showSelectIcon: true,
hintText: FlutterI18n.translate( hintText: FlutterI18n.translate(
context, context,
'patchesSelectorView.searchBarHint', 'patchesSelectorView.searchBarHint',
@ -113,12 +111,6 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
_query = searchQuery; _query = searchQuery;
}); });
}, },
onSelectAll: (value) {
if (value) {
model.selectAllPatcherWarning(context);
}
model.selectAllPatches(value);
},
), ),
), ),
), ),
@ -155,6 +147,9 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
CustomChip( CustomChip(
label: I18nText('patchesSelectorView.all'), label: I18nText('patchesSelectorView.all'),
onSelected: (value) { onSelected: (value) {
if (value) {
model.selectAllPatcherWarning(context);
}
model.selectAllPatches(true); model.selectAllPatches(true);
}, },
), ),

View File

@ -1,4 +1,5 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
@ -10,7 +11,6 @@ import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:flutter/material.dart';
class PatchesSelectorViewModel extends BaseViewModel { class PatchesSelectorViewModel extends BaseViewModel {
final PatcherAPI _patcherAPI = locator<PatcherAPI>(); final PatcherAPI _patcherAPI = locator<PatcherAPI>();
@ -26,9 +26,11 @@ class PatchesSelectorViewModel extends BaseViewModel {
Future<void> initialize() async { Future<void> initialize() async {
getPatchesVersion(); getPatchesVersion();
patches.addAll(_patcherAPI.getFilteredPatches( patches.addAll(
locator<PatcherViewModel>().selectedApp!.originalPackageName, _patcherAPI.getFilteredPatches(
)); locator<PatcherViewModel>().selectedApp!.originalPackageName,
),
);
patches.sort((a, b) => a.name.compareTo(b.name)); patches.sort((a, b) => a.name.compareTo(b.name));
notifyListeners(); notifyListeners();
} }
@ -84,8 +86,11 @@ class PatchesSelectorViewModel extends BaseViewModel {
selectedPatches.clear(); selectedPatches.clear();
if (_managerAPI.areExperimentalPatchesEnabled() == false) { if (_managerAPI.areExperimentalPatchesEnabled() == false) {
selectedPatches.addAll(patches.where( selectedPatches.addAll(
(element) => element.excluded == false && isPatchSupported(element))); patches.where(
(element) => element.excluded == false && isPatchSupported(element),
),
);
} }
if (_managerAPI.areExperimentalPatchesEnabled()) { if (_managerAPI.areExperimentalPatchesEnabled()) {
@ -117,15 +122,18 @@ class PatchesSelectorViewModel extends BaseViewModel {
patchesVersion = await _githubAPI patchesVersion = await _githubAPI
.getLastestReleaseVersion(_managerAPI.getPatchesRepo()); .getLastestReleaseVersion(_managerAPI.getPatchesRepo());
} }
return null;
} }
List<Patch> getQueriedPatches(String query) { List<Patch> getQueriedPatches(String query) {
return patches return patches
.where((patch) => .where(
query.isEmpty || (patch) =>
query.length < 2 || query.isEmpty ||
patch.name.toLowerCase().contains(query.toLowerCase()) || query.length < 2 ||
patch.getSimpleName().toLowerCase().contains(query.toLowerCase())) patch.name.toLowerCase().contains(query.toLowerCase()) ||
patch.getSimpleName().toLowerCase().contains(query.toLowerCase()),
)
.toList(); .toList();
} }
@ -134,8 +142,8 @@ class PatchesSelectorViewModel extends BaseViewModel {
} }
List<String> getSupportedVersions(Patch patch) { List<String> getSupportedVersions(Patch patch) {
PatchedApplication app = locator<PatcherViewModel>().selectedApp!; final PatchedApplication app = locator<PatcherViewModel>().selectedApp!;
Package? package = patch.compatiblePackages.firstWhereOrNull( final Package? package = patch.compatiblePackages.firstWhereOrNull(
(pack) => pack.name == app.packageName, (pack) => pack.name == app.packageName,
); );
if (package != null) { if (package != null) {
@ -146,10 +154,13 @@ class PatchesSelectorViewModel extends BaseViewModel {
} }
bool isPatchSupported(Patch patch) { bool isPatchSupported(Patch patch) {
PatchedApplication app = locator<PatcherViewModel>().selectedApp!; final PatchedApplication app = locator<PatcherViewModel>().selectedApp!;
return patch.compatiblePackages.isEmpty || patch.compatiblePackages.any((pack) => return patch.compatiblePackages.isEmpty ||
pack.name == app.packageName && patch.compatiblePackages.any(
(pack.versions.isEmpty || pack.versions.contains(app.version))); (pack) =>
pack.name == app.packageName &&
(pack.versions.isEmpty || pack.versions.contains(app.version)),
);
} }
void onMenuSelection(value) { void onMenuSelection(value) {
@ -161,20 +172,23 @@ class PatchesSelectorViewModel extends BaseViewModel {
} }
Future<void> saveSelectedPatches() async { Future<void> saveSelectedPatches() async {
List<String> selectedPatches = final List<String> selectedPatches =
this.selectedPatches.map((patch) => patch.name).toList(); this.selectedPatches.map((patch) => patch.name).toList();
await _managerAPI.setSelectedPatches( await _managerAPI.setSelectedPatches(
locator<PatcherViewModel>().selectedApp!.originalPackageName, locator<PatcherViewModel>().selectedApp!.originalPackageName,
selectedPatches); selectedPatches,
);
} }
Future<void> loadSelectedPatches() async { Future<void> loadSelectedPatches() async {
List<String> selectedPatches = await _managerAPI.getSelectedPatches( final List<String> selectedPatches = await _managerAPI.getSelectedPatches(
locator<PatcherViewModel>().selectedApp!.originalPackageName); locator<PatcherViewModel>().selectedApp!.originalPackageName,
);
if (selectedPatches.isNotEmpty) { if (selectedPatches.isNotEmpty) {
this.selectedPatches.clear(); this.selectedPatches.clear();
this.selectedPatches.addAll( this.selectedPatches.addAll(
patches.where((patch) => selectedPatches.contains(patch.name))); patches.where((patch) => selectedPatches.contains(patch.name)),
);
} else { } else {
locator<Toast>().showBottom('patchesSelectorView.noSavedPatches'); locator<Toast>().showBottom('patchesSelectorView.noSavedPatches');
} }

View File

@ -15,7 +15,7 @@ class SManageApiUrl extends BaseViewModel {
final TextEditingController _apiUrlController = TextEditingController(); final TextEditingController _apiUrlController = TextEditingController();
Future<void> showApiUrlDialog(BuildContext context) async { Future<void> showApiUrlDialog(BuildContext context) async {
String apiUrl = _managerAPI.getApiUrl(); final String apiUrl = _managerAPI.getApiUrl();
_apiUrlController.text = apiUrl.replaceAll('https://', ''); _apiUrlController.text = apiUrl.replaceAll('https://', '');
return showDialog( return showDialog(
context: context, context: context,

View File

@ -19,9 +19,9 @@ class SManageSources extends BaseViewModel {
final TextEditingController _intSourceController = TextEditingController(); final TextEditingController _intSourceController = TextEditingController();
Future<void> showSourcesDialog(BuildContext context) async { Future<void> showSourcesDialog(BuildContext context) async {
String hostRepository = _managerAPI.getRepoUrl(); final String hostRepository = _managerAPI.getRepoUrl();
String patchesRepo = _managerAPI.getPatchesRepo(); final String patchesRepo = _managerAPI.getPatchesRepo();
String integrationsRepo = _managerAPI.getIntegrationsRepo(); final String integrationsRepo = _managerAPI.getIntegrationsRepo();
_hostSourceController.text = hostRepository; _hostSourceController.text = hostRepository;
_orgPatSourceController.text = patchesRepo.split('/')[0]; _orgPatSourceController.text = patchesRepo.split('/')[0];
_patSourceController.text = patchesRepo.split('/')[1]; _patSourceController.text = patchesRepo.split('/')[1];

View File

@ -23,9 +23,9 @@ class SUpdateTheme extends BaseViewModel {
return _managerAPI.getUseDynamicTheme(); return _managerAPI.getUseDynamicTheme();
} }
void setUseDynamicTheme(BuildContext context, bool value) async { Future<void> setUseDynamicTheme(BuildContext context, bool value) async {
await _managerAPI.setUseDynamicTheme(value); await _managerAPI.setUseDynamicTheme(value);
int currentTheme = DynamicTheme.of(context)!.themeId; final int currentTheme = DynamicTheme.of(context)!.themeId;
if (currentTheme.isEven) { if (currentTheme.isEven) {
await DynamicTheme.of(context)!.setTheme(value ? 2 : 0); await DynamicTheme.of(context)!.setTheme(value ? 2 : 0);
} else { } else {
@ -38,9 +38,9 @@ class SUpdateTheme extends BaseViewModel {
return _managerAPI.getUseDarkTheme(); return _managerAPI.getUseDarkTheme();
} }
void setUseDarkTheme(BuildContext context, bool value) async { Future<void> setUseDarkTheme(BuildContext context, bool value) async {
await _managerAPI.setUseDarkTheme(value); await _managerAPI.setUseDarkTheme(value);
int currentTheme = DynamicTheme.of(context)!.themeId; final int currentTheme = DynamicTheme.of(context)!.themeId;
if (currentTheme < 2) { if (currentTheme < 2) {
await DynamicTheme.of(context)!.setTheme(value ? 1 : 0); await DynamicTheme.of(context)!.setTheme(value ? 1 : 0);
} else { } else {

View File

@ -3,7 +3,6 @@
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:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragement/settings_update_language.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragement/settings_update_theme.dart'; import 'package:revanced_manager/ui/views/settings/settingsFragement/settings_update_theme.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_advanced_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_advanced_section.dart';

View File

@ -70,19 +70,15 @@ class SettingsViewModel extends BaseViewModel {
Future<void> exportPatches() async { Future<void> exportPatches() async {
try { try {
File outFile = File(_managerAPI.storedPatchesFile); final File outFile = File(_managerAPI.storedPatchesFile);
if (outFile.existsSync()) { if (outFile.existsSync()) {
String dateTime = final String dateTime =
DateTime.now().toString().replaceAll(' ', '_').split('.').first; DateTime.now().toString().replaceAll(' ', '_').split('.').first;
String tempFilePath =
'${outFile.path.substring(0, outFile.path.lastIndexOf('/') + 1)}selected_patches_$dateTime.json';
outFile.copySync(tempFilePath);
await CRFileSaver.saveFileWithDialog(SaveFileDialogParams( await CRFileSaver.saveFileWithDialog(SaveFileDialogParams(
sourceFilePath: tempFilePath, destinationFileName: '')); sourceFilePath: outFile.path, destinationFileName: 'selected_patches_$dateTime.json',),);
File(tempFilePath).delete(); _toast.showBottom('settingsView.exportedPatches');
locator<Toast>().showBottom('settingsView.exportedPatches');
} else { } else {
locator<Toast>().showBottom('settingsView.noExportFileFound'); _toast.showBottom('settingsView.noExportFileFound');
} }
} on Exception catch (e, s) { } on Exception catch (e, s) {
Sentry.captureException(e, stackTrace: s); Sentry.captureException(e, stackTrace: s);
@ -91,26 +87,22 @@ class SettingsViewModel extends BaseViewModel {
Future<void> importPatches() async { Future<void> importPatches() async {
try { try {
FilePickerResult? result = await FilePicker.platform.pickFiles( final FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom, type: FileType.custom,
allowedExtensions: ['json'], allowedExtensions: ['json'],
); );
if (result != null && result.files.single.path != null) { if (result != null && result.files.single.path != null) {
File inFile = File(result.files.single.path!); final File inFile = File(result.files.single.path!);
final File storedPatchesFile = File(_managerAPI.storedPatchesFile); inFile.copySync(_managerAPI.storedPatchesFile);
if (!storedPatchesFile.existsSync()) {
storedPatchesFile.createSync(recursive: true);
}
inFile.copySync(storedPatchesFile.path);
inFile.delete(); inFile.delete();
if (locator<PatcherViewModel>().selectedApp != null) { if (locator<PatcherViewModel>().selectedApp != null) {
locator<PatcherViewModel>().loadLastSelectedPatches(); locator<PatcherViewModel>().loadLastSelectedPatches();
} }
locator<Toast>().showBottom('settingsView.importedPatches'); _toast.showBottom('settingsView.importedPatches');
} }
} on Exception catch (e, s) { } on Exception catch (e, s) {
await Sentry.captureException(e, stackTrace: s); await Sentry.captureException(e, stackTrace: s);
locator<Toast>().showBottom('settingsView.jsonSelectorErrorMessage'); _toast.showBottom('settingsView.jsonSelectorErrorMessage');
} }
} }
@ -120,13 +112,13 @@ class SettingsViewModel extends BaseViewModel {
} }
Future<int> getSdkVersion() async { Future<int> getSdkVersion() async {
AndroidDeviceInfo info = await DeviceInfoPlugin().androidInfo; final AndroidDeviceInfo info = await DeviceInfoPlugin().androidInfo;
return info.version.sdkInt ?? -1; return info.version.sdkInt ?? -1;
} }
Future<void> deleteLogs() async { Future<void> deleteLogs() async {
Directory appCacheDir = await getTemporaryDirectory(); final Directory appCacheDir = await getTemporaryDirectory();
Directory logsDir = Directory('${appCacheDir.path}/logs'); final Directory logsDir = Directory('${appCacheDir.path}/logs');
if (logsDir.existsSync()) { if (logsDir.existsSync()) {
logsDir.deleteSync(recursive: true); logsDir.deleteSync(recursive: true);
} }
@ -134,17 +126,18 @@ class SettingsViewModel extends BaseViewModel {
} }
Future<void> exportLogcatLogs() async { Future<void> exportLogcatLogs() async {
Directory appCache = await getTemporaryDirectory(); final Directory appCache = await getTemporaryDirectory();
Directory logDir = Directory('${appCache.path}/logs'); final Directory logDir = Directory('${appCache.path}/logs');
logDir.createSync(); logDir.createSync();
String dateTime = DateTime.now() final String dateTime = DateTime.now()
.toIso8601String() .toIso8601String()
.replaceAll('-', '') .replaceAll('-', '')
.replaceAll(':', '') .replaceAll(':', '')
.replaceAll('T', '') .replaceAll('T', '')
.replaceAll('.', ''); .replaceAll('.', '');
File logcat = File('${logDir.path}/revanced-manager_logcat_$dateTime.log'); final File logcat =
String logs = await Logcat.execute(); File('${logDir.path}/revanced-manager_logcat_$dateTime.log');
final String logs = await Logcat.execute();
logcat.writeAsStringSync(logs); logcat.writeAsStringSync(logs);
ShareExtend.share(logcat.path, 'file'); ShareExtend.share(logcat.path, 'file');
} }

View File

@ -8,12 +8,11 @@ import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
class AppInfoView extends StatelessWidget { class AppInfoView extends StatelessWidget {
final PatchedApplication app;
const AppInfoView({ const AppInfoView({
Key? key, Key? key,
required this.app, required this.app,
}) : super(key: key); }) : super(key: key);
final PatchedApplication app;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -29,7 +29,7 @@ class AppInfoViewModel extends BaseViewModel {
) async { ) async {
bool isUninstalled = true; bool isUninstalled = true;
if (app.isRooted) { if (app.isRooted) {
bool hasRootPermissions = await _rootAPI.hasRootPermissions(); final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) { if (hasRootPermissions) {
await _rootAPI.deleteApp(app.packageName, app.apkFilePath); await _rootAPI.deleteApp(app.packageName, app.apkFilePath);
if (!onlyUnpatch) { if (!onlyUnpatch) {
@ -45,7 +45,7 @@ class AppInfoViewModel extends BaseViewModel {
} }
} }
void navigateToPatcher(PatchedApplication app) async { Future<void> navigateToPatcher(PatchedApplication app) async {
locator<PatcherViewModel>().selectedApp = app; locator<PatcherViewModel>().selectedApp = app;
locator<PatcherViewModel>().selectedPatches = locator<PatcherViewModel>().selectedPatches =
await _patcherAPI.getAppliedPatches(app.appliedPatches); await _patcherAPI.getAppliedPatches(app.appliedPatches);
@ -62,7 +62,7 @@ class AppInfoViewModel extends BaseViewModel {
PatchedApplication app, PatchedApplication app,
bool onlyUnpatch, bool onlyUnpatch,
) async { ) async {
bool hasRootPermissions = await _rootAPI.hasRootPermissions(); final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (app.isRooted && !hasRootPermissions) { if (app.isRooted && !hasRootPermissions) {
return showDialog( return showDialog(
context: context, context: context,
@ -134,7 +134,8 @@ class AppInfoViewModel extends BaseViewModel {
title: I18nText('appInfoView.appliedPatchesLabel'), title: I18nText('appInfoView.appliedPatchesLabel'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer, backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: SingleChildScrollView( content: SingleChildScrollView(
child: Text(getAppliedPatchesString(app.appliedPatches))), child: Text(getAppliedPatchesString(app.appliedPatches)),
),
actions: <Widget>[ actions: <Widget>[
CustomMaterialButton( CustomMaterialButton(
label: I18nText('okButton'), label: I18nText('okButton'),
@ -146,13 +147,15 @@ class AppInfoViewModel extends BaseViewModel {
} }
String getAppliedPatchesString(List<String> appliedPatches) { String getAppliedPatchesString(List<String> appliedPatches) {
List<String> names = appliedPatches final List<String> names = appliedPatches
.map((p) => p .map(
.replaceAll('-', ' ') (p) => p
.split('-') .replaceAll('-', ' ')
.join(' ') .split('-')
.toTitleCase() .join(' ')
.replaceFirst('Microg', 'MicroG')) .toTitleCase()
.replaceFirst('Microg', 'MicroG'),
)
.toList(); .toList();
return '\u2022 ${names.join('\n\u2022 ')}'; return '\u2022 ${names.join('\n\u2022 ')}';
} }

View File

@ -21,7 +21,6 @@ class AppSkeletonLoader extends StatelessWidget {
style: SkeletonAvatarStyle( style: SkeletonAvatarStyle(
width: screenWidth * 0.15, width: screenWidth * 0.15,
height: screenWidth * 0.15, height: screenWidth * 0.15,
shape: BoxShape.rectangle,
borderRadius: const BorderRadius.all(Radius.circular(12)), borderRadius: const BorderRadius.all(Radius.circular(12)),
), ),
), ),

View File

@ -3,12 +3,6 @@ import 'package:flutter/material.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
class InstalledAppItem extends StatefulWidget { class InstalledAppItem extends StatefulWidget {
final String name;
final String pkgName;
final Uint8List icon;
final int patchesCount;
final Function()? onTap;
const InstalledAppItem({ const InstalledAppItem({
Key? key, Key? key,
required this.name, required this.name,
@ -17,6 +11,11 @@ class InstalledAppItem extends StatefulWidget {
required this.patchesCount, required this.patchesCount,
this.onTap, this.onTap,
}) : super(key: key); }) : super(key: key);
final String name;
final String pkgName;
final Uint8List icon;
final int patchesCount;
final Function()? onTap;
@override @override
State<InstalledAppItem> createState() => _InstalledAppItemState(); State<InstalledAppItem> createState() => _InstalledAppItemState();
@ -48,7 +47,6 @@ class _InstalledAppItemState extends State<InstalledAppItem> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Text( Text(
widget.name, widget.name,
@ -62,8 +60,8 @@ class _InstalledAppItemState extends State<InstalledAppItem> {
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
widget.patchesCount == 1 widget.patchesCount == 1
? "${widget.patchesCount} patch" ? '${widget.patchesCount} patch'
: "${widget.patchesCount} patches", : '${widget.patchesCount} patches',
style: TextStyle( style: TextStyle(
fontSize: 8, fontSize: 8,
color: Theme.of(context).colorScheme.secondary, color: Theme.of(context).colorScheme.secondary,

View File

@ -6,14 +6,13 @@ import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class ContributorsCard extends StatefulWidget { class ContributorsCard extends StatefulWidget {
final String title;
final List<dynamic> contributors;
const ContributorsCard({ const ContributorsCard({
Key? key, Key? key,
required this.title, required this.title,
required this.contributors, required this.contributors,
}) : super(key: key); }) : super(key: key);
final String title;
final List<dynamic> contributors;
@override @override
State<ContributorsCard> createState() => _ContributorsCardState(); State<ContributorsCard> createState() => _ContributorsCardState();

View File

@ -3,7 +3,6 @@ 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/models/patched_application.dart'; import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart'; import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/application_item.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
class AvailableUpdatesCard extends StatelessWidget { class AvailableUpdatesCard extends StatelessWidget {

View File

@ -2,16 +2,15 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; 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/ui/views/home/home_viewmodel.dart'; import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.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';
class LatestCommitCard extends StatefulWidget { class LatestCommitCard extends StatefulWidget {
final Function() onPressed;
const LatestCommitCard({ const LatestCommitCard({
Key? key, Key? key,
required this.onPressed, required this.onPressed,
}) : super(key: key); }) : super(key: key);
final Function() onPressed;
@override @override
State<LatestCommitCard> createState() => _LatestCommitCardState(); State<LatestCommitCard> createState() => _LatestCommitCardState();
@ -73,7 +72,6 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
builder: (context, snapshot) => Opacity( builder: (context, snapshot) => Opacity(
opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.25, opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.25,
child: CustomMaterialButton( child: CustomMaterialButton(
isExpanded: false,
label: I18nText('latestCommitCard.updateButton'), label: I18nText('latestCommitCard.updateButton'),
onPressed: snapshot.hasData && snapshot.data! onPressed: snapshot.hasData && snapshot.data!
? widget.onPressed ? widget.onPressed

View File

@ -14,7 +14,6 @@ class UpdateConfirmationDialog extends StatelessWidget {
return DraggableScrollableSheet( return DraggableScrollableSheet(
expand: false, expand: false,
initialChildSize: 0.5,
snap: true, snap: true,
snapSizes: const [0.5], snapSizes: const [0.5],
builder: (context, scrollController) => SingleChildScrollView( builder: (context, scrollController) => SingleChildScrollView(
@ -37,7 +36,11 @@ class UpdateConfirmationDialog extends StatelessWidget {
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 40.0, left: 24.0, right: 24.0, bottom: 32.0), top: 40.0,
left: 24.0,
right: 24.0,
bottom: 32.0,
),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
@ -47,10 +50,11 @@ class UpdateConfirmationDialog extends StatelessWidget {
I18nText( I18nText(
'homeView.updateDialogTitle', 'homeView.updateDialogTitle',
child: const Text( child: const Text(
"", '',
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold), fontWeight: FontWeight.bold,
),
), ),
), ),
const SizedBox(height: 4.0), const SizedBox(height: 4.0),
@ -63,7 +67,7 @@ class UpdateConfirmationDialog extends StatelessWidget {
), ),
const SizedBox(width: 8.0), const SizedBox(width: 8.0),
Text( Text(
snapshot.data!["tag_name"] ?? "Unknown", snapshot.data!['tag_name'] ?? 'Unknown',
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -93,13 +97,14 @@ class UpdateConfirmationDialog extends StatelessWidget {
child: I18nText( child: I18nText(
'homeView.updateChangelogTitle', 'homeView.updateChangelogTitle',
child: Text( child: Text(
"", '',
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Theme.of(context) color: Theme.of(context)
.colorScheme .colorScheme
.onSecondaryContainer), .onSecondaryContainer,
),
), ),
), ),
), ),
@ -113,7 +118,7 @@ class UpdateConfirmationDialog extends StatelessWidget {
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
data: snapshot.data!["body"] ?? "", data: snapshot.data!['body'] ?? '',
), ),
), ),
], ],

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class GradientProgressIndicator extends StatefulWidget { class GradientProgressIndicator extends StatefulWidget {
final double? progress;
const GradientProgressIndicator({required this.progress, super.key}); const GradientProgressIndicator({required this.progress, super.key});
final double? progress;
@override @override
State<GradientProgressIndicator> createState() => State<GradientProgressIndicator> createState() =>

View File

@ -6,12 +6,11 @@ import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
class AppSelectorCard extends StatelessWidget { class AppSelectorCard extends StatelessWidget {
final Function() onPressed;
const AppSelectorCard({ const AppSelectorCard({
Key? key, Key? key,
required this.onPressed, required this.onPressed,
}) : super(key: key); }) : super(key: key);
final Function() onPressed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -33,40 +32,41 @@ class AppSelectorCard extends StatelessWidget {
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
locator<PatcherViewModel>().selectedApp == null if (locator<PatcherViewModel>().selectedApp == null)
? I18nText('appSelectorCard.widgetSubtitle') I18nText('appSelectorCard.widgetSubtitle')
: Row( else
children: <Widget>[ Row(
SizedBox( children: <Widget>[
height: 18.0, SizedBox(
child: ClipOval( height: 18.0,
child: Image.memory( child: ClipOval(
locator<PatcherViewModel>().selectedApp == null child: Image.memory(
? Uint8List(0) locator<PatcherViewModel>().selectedApp == null
: locator<PatcherViewModel>().selectedApp!.icon, ? Uint8List(0)
fit: BoxFit.cover, : locator<PatcherViewModel>().selectedApp!.icon,
), fit: BoxFit.cover,
),
), ),
const SizedBox(width: 6), ),
Text(
locator<PatcherViewModel>()
.getAppSelectionString(),
style: const TextStyle(fontWeight: FontWeight.w600),
),
],
), ),
locator<PatcherViewModel>().selectedApp == null const SizedBox(width: 6),
? Container() Text(
: Column( locator<PatcherViewModel>().getAppSelectionString(),
children: [ style: const TextStyle(fontWeight: FontWeight.w600),
const SizedBox(height: 4),
Text(
locator<PatcherViewModel>()
.getRecommendedVersionString(context),
),
],
), ),
],
),
if (locator<PatcherViewModel>().selectedApp == null)
Container()
else
Column(
children: [
const SizedBox(height: 4),
Text(
locator<PatcherViewModel>()
.getRecommendedVersionString(context),
),
],
),
], ],
), ),
); );

View File

@ -6,12 +6,11 @@ import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
class PatchSelectorCard extends StatelessWidget { class PatchSelectorCard extends StatelessWidget {
final Function() onPressed;
const PatchSelectorCard({ const PatchSelectorCard({
Key? key, Key? key,
required this.onPressed, required this.onPressed,
}) : super(key: key); }) : super(key: key);
final Function() onPressed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -46,11 +45,12 @@ class PatchSelectorCard extends StatelessWidget {
], ],
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
locator<PatcherViewModel>().selectedApp == null if (locator<PatcherViewModel>().selectedApp == null)
? I18nText('patchSelectorCard.widgetSubtitle') I18nText('patchSelectorCard.widgetSubtitle')
: locator<PatcherViewModel>().selectedPatches.isEmpty else
? I18nText('patchSelectorCard.widgetEmptySubtitle') locator<PatcherViewModel>().selectedPatches.isEmpty
: Text(_getPatchesSelection()), ? I18nText('patchSelectorCard.widgetEmptySubtitle')
: Text(_getPatchesSelection()),
], ],
), ),
); );
@ -58,7 +58,7 @@ class PatchSelectorCard extends StatelessWidget {
String _getPatchesSelection() { String _getPatchesSelection() {
String text = ''; String text = '';
for (Patch p in locator<PatcherViewModel>().selectedPatches) { for (final Patch p in locator<PatcherViewModel>().selectedPatches) {
text += '\u2022 ${p.getSimpleName()} (v${p.version})\n'; text += '\u2022 ${p.getSimpleName()} (v${p.version})\n';
} }
return text.substring(0, text.length - 1); return text.substring(0, text.length - 1);

View File

@ -3,11 +3,24 @@ 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/shared/custom_material_button.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';
// ignore: must_be_immutable // ignore: must_be_immutable
class PatchItem extends StatefulWidget { class PatchItem extends StatefulWidget {
PatchItem({
Key? key,
required this.name,
required this.simpleName,
required this.description,
required this.version,
required this.packageVersion,
required this.supportedPackageVersions,
required this.isUnsupported,
required this.isSelected,
required this.onChanged,
this.child,
}) : super(key: key);
final String name; final String name;
final String simpleName; final String simpleName;
final String description; final String description;
@ -21,20 +34,6 @@ class PatchItem extends StatefulWidget {
final toast = locator<Toast>(); final toast = locator<Toast>();
final _managerAPI = locator<ManagerAPI>(); final _managerAPI = locator<ManagerAPI>();
PatchItem(
{Key? key,
required this.name,
required this.simpleName,
required this.description,
required this.version,
required this.packageVersion,
required this.supportedPackageVersions,
required this.isUnsupported,
required this.isSelected,
required this.onChanged,
this.child})
: super(key: key);
@override @override
State<PatchItem> createState() => _PatchItemState(); State<PatchItem> createState() => _PatchItemState();
} }
@ -75,25 +74,27 @@ class _PatchItemState extends State<PatchItem> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[ children: <Widget>[
Text( Expanded(
widget.simpleName, child: Text(
maxLines: 2, widget.simpleName,
overflow: TextOverflow.visible, maxLines: 2,
style: const TextStyle( overflow: TextOverflow.visible,
fontSize: 16, style: const TextStyle(
fontWeight: FontWeight.w600, fontSize: 16,
), fontWeight: FontWeight.w600,
), ),
const SizedBox(width: 6),
Text(
widget.version,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).colorScheme.secondary,
), ),
), ),
], ],
), ),
const SizedBox(height: 1),
Text(
widget.version,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.secondary,
),
),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
widget.description, widget.description,
@ -139,38 +140,37 @@ class _PatchItemState extends State<PatchItem> {
) )
], ],
), ),
widget.isUnsupported if (widget.isUnsupported)
? Row( Row(
children: <Widget>[ children: <Widget>[
Padding( Padding(
padding: const EdgeInsets.only(top: 8), padding: const EdgeInsets.only(top: 8),
child: TextButton.icon( child: TextButton.icon(
label: I18nText('warning'), label: I18nText('warning'),
icon: const Icon(Icons.warning, size: 20.0), icon: const Icon(Icons.warning, size: 20.0),
onPressed: () => _showUnsupportedWarningDialog(), onPressed: () => _showUnsupportedWarningDialog(),
style: ButtonStyle( style: ButtonStyle(
shape: MaterialStateProperty.all( shape: MaterialStateProperty.all(
RoundedRectangleBorder( RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide( side: BorderSide(
width: 1, color: Theme.of(context).colorScheme.secondary,
color:
Theme.of(context).colorScheme.secondary,
),
),
),
backgroundColor: MaterialStateProperty.all(
Colors.transparent,
),
foregroundColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.secondary,
), ),
), ),
), ),
backgroundColor: MaterialStateProperty.all(
Colors.transparent,
),
foregroundColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.secondary,
),
), ),
], ),
) ),
: Container(), ],
)
else
Container(),
widget.child ?? const SizedBox(), widget.child ?? const SizedBox(),
], ],
), ),
@ -201,4 +201,4 @@ class _PatchItemState extends State<PatchItem> {
), ),
); );
} }
} }

View File

@ -3,8 +3,8 @@ import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
class OptionsTextField extends StatelessWidget { class OptionsTextField extends StatelessWidget {
final String hint;
const OptionsTextField({Key? key, required this.hint}) : super(key: key); const OptionsTextField({Key? key, required this.hint}) : super(key: key);
final String hint;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -12,7 +12,7 @@ class OptionsTextField extends StatelessWidget {
final sWidth = MediaQuery.of(context).size.width; final sWidth = MediaQuery.of(context).size.width;
return Container( return Container(
margin: const EdgeInsets.only(top: 12, bottom: 6), margin: const EdgeInsets.only(top: 12, bottom: 6),
padding: const EdgeInsets.all(0), padding: EdgeInsets.zero,
child: TextField( child: TextField(
decoration: InputDecoration( decoration: InputDecoration(
constraints: BoxConstraints( constraints: BoxConstraints(
@ -28,9 +28,9 @@ class OptionsTextField extends StatelessWidget {
} }
class OptionsFilePicker extends StatelessWidget { class OptionsFilePicker extends StatelessWidget {
final String optionName;
const OptionsFilePicker({Key? key, required this.optionName}) const OptionsFilePicker({Key? key, required this.optionName})
: super(key: key); : super(key: key);
final String optionName;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/utils/about_info.dart'; import 'package:revanced_manager/utils/about_info.dart';
import 'package:flutter/services.dart';
class AboutWidget extends StatefulWidget { class AboutWidget extends StatefulWidget {
const AboutWidget({Key? key, this.padding}) : super(key: key); const AboutWidget({Key? key, this.padding}) : super(key: key);

View File

@ -1,14 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CustomSwitch extends StatelessWidget { class CustomSwitch extends StatelessWidget {
final ValueChanged<bool> onChanged;
final bool value;
const CustomSwitch({ const CustomSwitch({
Key? key, Key? key,
required this.onChanged, required this.onChanged,
required this.value, required this.value,
}) : super(key: key); }) : super(key: key);
final ValueChanged<bool> onChanged;
final bool value;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -45,7 +44,7 @@ class CustomSwitch extends StatelessWidget {
shape: BoxShape.circle, shape: BoxShape.circle,
color: value color: value
? Theme.of(context).colorScheme.primaryContainer ? Theme.of(context).colorScheme.primaryContainer
: Colors.white, : Theme.of(context).colorScheme.surface,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black12.withOpacity(0.1), color: Colors.black12.withOpacity(0.1),

View File

@ -2,12 +2,6 @@ import 'package:flutter/material.dart';
import 'package:revanced_manager/ui/widgets/settingsView/custom_switch.dart'; import 'package:revanced_manager/ui/widgets/settingsView/custom_switch.dart';
class CustomSwitchTile extends StatelessWidget { class CustomSwitchTile extends StatelessWidget {
final Widget title;
final Widget subtitle;
final bool value;
final Function(bool) onTap;
final EdgeInsetsGeometry? padding;
const CustomSwitchTile({ const CustomSwitchTile({
Key? key, Key? key,
required this.title, required this.title,
@ -16,6 +10,11 @@ class CustomSwitchTile extends StatelessWidget {
required this.onTap, required this.onTap,
this.padding, this.padding,
}) : super(key: key); }) : super(key: key);
final Widget title;
final Widget subtitle;
final bool value;
final Function(bool) onTap;
final EdgeInsetsGeometry? padding;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,12 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CustomTextField extends StatelessWidget { class CustomTextField extends StatelessWidget {
final TextEditingController inputController;
final Widget label;
final String hint;
final Widget? leadingIcon;
final Function(String)? onChanged;
const CustomTextField({ const CustomTextField({
Key? key, Key? key,
required this.inputController, required this.inputController,
@ -15,6 +9,11 @@ class CustomTextField extends StatelessWidget {
this.leadingIcon, this.leadingIcon,
required this.onChanged, required this.onChanged,
}) : super(key: key); }) : super(key: key);
final TextEditingController inputController;
final Widget label;
final String hint;
final Widget? leadingIcon;
final Function(String)? onChanged;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -45,10 +44,8 @@ class CustomTextField extends StatelessWidget {
border: OutlineInputBorder( border: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
width: 1.0,
), ),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
gapPadding: 4.0,
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
@ -60,14 +57,12 @@ class CustomTextField extends StatelessWidget {
errorBorder: OutlineInputBorder( errorBorder: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).colorScheme.error, color: Theme.of(context).colorScheme.error,
width: 1.0,
), ),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
width: 1.0,
), ),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),

View File

@ -7,12 +7,14 @@ class SExperimentalUniversalPatches extends StatefulWidget {
const SExperimentalUniversalPatches({super.key}); const SExperimentalUniversalPatches({super.key});
@override @override
State<SExperimentalUniversalPatches> createState() => _SExperimentalUniversalPatchesState(); State<SExperimentalUniversalPatches> createState() =>
_SExperimentalUniversalPatchesState();
} }
final _settingsViewModel = SettingsViewModel(); final _settingsViewModel = SettingsViewModel();
class _SExperimentalUniversalPatchesState extends State<SExperimentalUniversalPatches> { class _SExperimentalUniversalPatchesState
extends State<SExperimentalUniversalPatches> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CustomSwitchTile( return CustomSwitchTile(

View File

@ -2,14 +2,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
class SettingsSection extends StatelessWidget { class SettingsSection extends StatelessWidget {
final String title;
final List<Widget> children;
const SettingsSection({ const SettingsSection({
Key? key, Key? key,
required this.title, required this.title,
required this.children, required this.children,
}) : super(key: key); }) : super(key: key);
final String title;
final List<Widget> children;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -2,11 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
class SettingsTileDialog extends StatelessWidget { class SettingsTileDialog extends StatelessWidget {
final String title;
final String subtitle;
final Function()? onTap;
final EdgeInsetsGeometry? padding;
const SettingsTileDialog({ const SettingsTileDialog({
Key? key, Key? key,
required this.title, required this.title,
@ -14,6 +9,10 @@ class SettingsTileDialog extends StatelessWidget {
required this.onTap, required this.onTap,
this.padding, this.padding,
}) : super(key: key); }) : super(key: key);
final String title;
final String subtitle;
final Function()? onTap;
final EdgeInsetsGeometry? padding;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,13 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class SocialMediaItem extends StatelessWidget { class SocialMediaItem extends StatelessWidget {
final Widget? icon;
final Widget title;
final Widget? subtitle;
final String? url;
const SocialMediaItem({ const SocialMediaItem({
Key? key, Key? key,
this.icon, this.icon,
@ -15,6 +9,10 @@ class SocialMediaItem extends StatelessWidget {
this.subtitle, this.subtitle,
this.url, this.url,
}) : super(key: key); }) : super(key: key);
final Widget? icon;
final Widget title;
final Widget? subtitle;
final String? url;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -6,12 +6,11 @@ import 'package:revanced_manager/ui/widgets/settingsView/social_media_item.dart'
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
class SocialMediaWidget extends StatelessWidget { class SocialMediaWidget extends StatelessWidget {
final EdgeInsetsGeometry? padding;
const SocialMediaWidget({ const SocialMediaWidget({
Key? key, Key? key,
this.padding, this.padding,
}) : super(key: key); }) : super(key: key);
final EdgeInsetsGeometry? padding;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,19 +1,13 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:expandable/expandable.dart';
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_material_button.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
import 'package:expandable/expandable.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:timeago/timeago.dart'; import 'package:timeago/timeago.dart';
class ApplicationItem extends StatefulWidget { class ApplicationItem extends StatefulWidget {
final Uint8List icon;
final String name;
final DateTime patchDate;
final List<String> changelog;
final bool isUpdatableApp;
final Function() onPressed;
const ApplicationItem({ const ApplicationItem({
Key? key, Key? key,
required this.icon, required this.icon,
@ -23,6 +17,12 @@ class ApplicationItem extends StatefulWidget {
required this.isUpdatableApp, required this.isUpdatableApp,
required this.onPressed, required this.onPressed,
}) : super(key: key); }) : super(key: key);
final Uint8List icon;
final String name;
final DateTime patchDate;
final List<String> changelog;
final bool isUpdatableApp;
final Function() onPressed;
@override @override
State<ApplicationItem> createState() => _ApplicationItemState(); State<ApplicationItem> createState() => _ApplicationItemState();
@ -33,7 +33,7 @@ class _ApplicationItemState extends State<ApplicationItem>
late AnimationController _animationController; late AnimationController _animationController;
@override @override
initState() { void initState() {
super.initState(); super.initState();
_animationController = AnimationController( _animationController = AnimationController(
vsync: this, vsync: this,
@ -49,95 +49,100 @@ class _ApplicationItemState extends State<ApplicationItem>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ExpandableController expController = ExpandableController(); final ExpandableController expController = ExpandableController();
return Container( return Container(
margin: const EdgeInsets.only(bottom: 16.0), margin: const EdgeInsets.only(bottom: 16.0),
child: CustomCard( child: CustomCard(
onTap: () { onTap: () {
expController.toggle(); expController.toggle();
_animationController.isCompleted _animationController.isCompleted
? _animationController.reverse() ? _animationController.reverse()
: _animationController.forward(); : _animationController.forward();
}, },
child: ExpandablePanel( child: ExpandablePanel(
controller: expController, controller: expController,
theme: const ExpandableThemeData( theme: const ExpandableThemeData(
inkWellBorderRadius: BorderRadius.all(Radius.circular(16)), inkWellBorderRadius: BorderRadius.all(Radius.circular(16)),
tapBodyToCollapse: false, tapBodyToCollapse: false,
tapBodyToExpand: false, tapBodyToExpand: false,
tapHeaderToExpand: false, tapHeaderToExpand: false,
hasIcon: false, hasIcon: false,
animationDuration: Duration(milliseconds: 450), animationDuration: Duration(milliseconds: 450),
), ),
header: Row( header: Row(
children: <Widget>[ children: <Widget>[
SizedBox( SizedBox(
width: 40, width: 40,
child: Image.memory(widget.icon, height: 40, width: 40), child: Image.memory(widget.icon, height: 40, width: 40),
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Padding( Padding(
padding: const EdgeInsets.only(left: 15.0), padding: const EdgeInsets.only(left: 15.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.name.length > 12
? '${widget.name.substring(0, 12)}...'
: widget.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
Text(format(widget.patchDate)),
],
),
),
const Spacer(),
RotationTransition(
turns: Tween(begin: 0.0, end: 0.50)
.animate(_animationController),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.arrow_drop_down),
),
),
const SizedBox(width: 8),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[ children: <Widget>[
CustomMaterialButton( Text(
label: widget.isUpdatableApp widget.name.length > 12
? I18nText('applicationItem.patchButton') ? '${widget.name.substring(0, 12)}...'
: I18nText('applicationItem.infoButton'), : widget.name,
onPressed: widget.onPressed, style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
), ),
Text(format(widget.patchDate)),
], ],
), ),
], ),
), const Spacer(),
collapsed: const SizedBox(), RotationTransition(
expanded: Padding( turns:
padding: const EdgeInsets.only( Tween(begin: 0.0, end: 0.50).animate(_animationController),
top: 16.0, left: 4.0, right: 4.0, bottom: 4.0), child: const Padding(
child: Column( padding: EdgeInsets.all(8.0),
crossAxisAlignment: CrossAxisAlignment.start, child: Icon(Icons.arrow_drop_down),
),
),
const SizedBox(width: 8),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[ children: <Widget>[
I18nText( CustomMaterialButton(
'applicationItem.changelogLabel', label: widget.isUpdatableApp
child: const Text( ? I18nText('applicationItem.patchButton')
'', : I18nText('applicationItem.infoButton'),
style: TextStyle(fontWeight: FontWeight.w700), onPressed: widget.onPressed,
),
), ),
const SizedBox(height: 4),
Text('\u2022 ${widget.changelog.join('\n\u2022 ')}'),
], ],
), ),
],
),
collapsed: const SizedBox(),
expanded: Padding(
padding: const EdgeInsets.only(
top: 16.0,
left: 4.0,
right: 4.0,
bottom: 4.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
I18nText(
'applicationItem.changelogLabel',
child: const Text(
'',
style: TextStyle(fontWeight: FontWeight.w700),
),
),
const SizedBox(height: 4),
Text('\u2022 ${widget.changelog.join('\n\u2022 ')}'),
],
), ),
), ),
)); ),
),
);
} }
} }

View File

@ -1,21 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CustomCard extends StatelessWidget { class CustomCard extends StatelessWidget {
const CustomCard({
Key? key,
this.isFilled = true,
required this.child,
this.onTap,
this.padding,
this.backgroundColor,
}) : super(key: key);
final bool isFilled; final bool isFilled;
final Widget child; final Widget child;
final Function()? onTap; final Function()? onTap;
final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? padding;
final Color? backgroundColor; final Color? backgroundColor;
const CustomCard(
{Key? key,
this.isFilled = true,
required this.child,
this.onTap,
this.padding,
this.backgroundColor})
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Material(

View File

@ -1,16 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CustomChip extends StatelessWidget { class CustomChip extends StatelessWidget {
final Widget label;
final bool isSelected;
final Function(bool)? onSelected;
const CustomChip({ const CustomChip({
Key? key, Key? key,
required this.label, required this.label,
this.isSelected = false, this.isSelected = false,
this.onSelected, this.onSelected,
}) : super(key: key); }) : super(key: key);
final Widget label;
final bool isSelected;
final Function(bool)? onSelected;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,11 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CustomMaterialButton extends StatelessWidget { class CustomMaterialButton extends StatelessWidget {
final Widget label;
final bool isFilled;
final bool isExpanded;
final Function()? onPressed;
const CustomMaterialButton({ const CustomMaterialButton({
Key? key, Key? key,
required this.label, required this.label,
@ -13,6 +8,10 @@ class CustomMaterialButton extends StatelessWidget {
this.isExpanded = false, this.isExpanded = false,
required this.onPressed, required this.onPressed,
}) : super(key: key); }) : super(key: key);
final Widget label;
final bool isFilled;
final bool isExpanded;
final Function()? onPressed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -28,7 +27,6 @@ class CustomMaterialButton extends StatelessWidget {
side: isFilled side: isFilled
? BorderSide.none ? BorderSide.none
: BorderSide( : BorderSide(
width: 1,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
), ),
@ -50,12 +48,6 @@ class CustomMaterialButton extends StatelessWidget {
// ignore: must_be_immutable // ignore: must_be_immutable
class TimerButton extends StatefulWidget { class TimerButton extends StatefulWidget {
Widget label;
bool isFilled;
int seconds;
final bool isRunning;
final Function()? onTimerEnd;
TimerButton({ TimerButton({
Key? key, Key? key,
required this.seconds, required this.seconds,
@ -64,6 +56,11 @@ class TimerButton extends StatefulWidget {
this.label = const Text(''), this.label = const Text(''),
this.isFilled = true, this.isFilled = true,
}) : super(key: key); }) : super(key: key);
Widget label;
bool isFilled;
int seconds;
final bool isRunning;
final Function()? onTimerEnd;
@override @override
State<TimerButton> createState() => _TimerButtonState(); State<TimerButton> createState() => _TimerButtonState();
@ -101,7 +98,6 @@ class _TimerButtonState extends State<TimerButton> {
side: widget.isFilled side: widget.isFilled
? BorderSide.none ? BorderSide.none
: BorderSide( : BorderSide(
width: 1,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
), ),

View File

@ -1,14 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CustomPopupMenu extends StatelessWidget { class CustomPopupMenu extends StatelessWidget {
final Function(dynamic) onSelected;
final Map<int, Widget> children;
const CustomPopupMenu({ const CustomPopupMenu({
Key? key, Key? key,
required this.onSelected, required this.onSelected,
required this.children, required this.children,
}) : super(key: key); }) : super(key: key);
final Function(dynamic) onSelected;
final Map<int, Widget> children;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,12 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CustomSliverAppBar extends StatelessWidget { class CustomSliverAppBar extends StatelessWidget {
final Widget title;
final List<Widget>? actions;
final PreferredSizeWidget? bottom;
final bool isMainView;
final Function()? onBackButtonPressed;
const CustomSliverAppBar({ const CustomSliverAppBar({
Key? key, Key? key,
required this.title, required this.title,
@ -15,13 +9,16 @@ class CustomSliverAppBar extends StatelessWidget {
this.isMainView = false, this.isMainView = false,
this.onBackButtonPressed, this.onBackButtonPressed,
}) : super(key: key); }) : super(key: key);
final Widget title;
final List<Widget>? actions;
final PreferredSizeWidget? bottom;
final bool isMainView;
final Function()? onBackButtonPressed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SliverAppBar( return SliverAppBar(
pinned: true, pinned: true,
snap: false,
floating: false,
expandedHeight: 100.0, expandedHeight: 100.0,
automaticallyImplyLeading: !isMainView, automaticallyImplyLeading: !isMainView,
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(

View File

@ -2,21 +2,19 @@ import 'package:animations/animations.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class OpenContainerWrapper extends StatelessWidget { class OpenContainerWrapper extends StatelessWidget {
final OpenContainerBuilder openBuilder;
final CloseContainerBuilder closedBuilder;
const OpenContainerWrapper({ const OpenContainerWrapper({
Key? key, Key? key,
required this.openBuilder, required this.openBuilder,
required this.closedBuilder, required this.closedBuilder,
}) : super(key: key); }) : super(key: key);
final OpenContainerBuilder openBuilder;
final CloseContainerBuilder closedBuilder;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return OpenContainer( return OpenContainer(
openBuilder: openBuilder, openBuilder: openBuilder,
closedBuilder: closedBuilder, closedBuilder: closedBuilder,
transitionType: ContainerTransitionType.fade,
transitionDuration: const Duration(milliseconds: 400), transitionDuration: const Duration(milliseconds: 400),
openColor: Theme.of(context).colorScheme.primary, openColor: Theme.of(context).colorScheme.primary,
closedColor: Colors.transparent, closedColor: Colors.transparent,

View File

@ -1,10 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class SearchBar extends StatefulWidget { class SearchBar extends StatefulWidget {
final String? hintText;
final bool showSelectIcon;
final Function(bool)? onSelectAll;
const SearchBar({ const SearchBar({
Key? key, Key? key,
required this.hintText, required this.hintText,
@ -12,6 +8,9 @@ class SearchBar extends StatefulWidget {
this.onSelectAll, this.onSelectAll,
required this.onQueryChanged, required this.onQueryChanged,
}) : super(key: key); }) : super(key: key);
final String? hintText;
final bool showSelectIcon;
final Function(bool)? onSelectAll;
final Function(String) onQueryChanged; final Function(String) onQueryChanged;

View File

@ -1,6 +1,6 @@
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:device_info_plus/device_info_plus.dart';
class AboutInfo { class AboutInfo {
static Future<Map<String, dynamic>> getInfo() async { static Future<Map<String, dynamic>> getInfo() async {

View File

@ -2,4 +2,4 @@
class Environment { class Environment {
static const sentryDSN = ''; static const sentryDSN = '';
static const crowdinKEY = ''; static const crowdinKEY = '';
} }

View File

@ -4,7 +4,7 @@ homepage: https://github.com/revanced/revanced-manager
publish_to: 'none' publish_to: 'none'
version: 0.0.51+51 version: 0.0.55+55
environment: environment:
sdk: ">=2.17.5 <3.0.0" sdk: ">=2.17.5 <3.0.0"
@ -15,7 +15,7 @@ dependencies:
app_installer: ^1.1.0 app_installer: ^1.1.0
collection: ^1.16.0 collection: ^1.16.0
cross_connectivity: ^3.0.5 cross_connectivity: ^3.0.5
cr_file_saver: ^0.0.1+2 cr_file_saver: ^0.0.2
device_apps: device_apps:
git: git:
url: https://github.com/ponces/flutter_plugin_device_apps url: https://github.com/ponces/flutter_plugin_device_apps