Compare commits

..

35 Commits

Author SHA1 Message Date
Imran
7071e34a74 Merge pull request #1643 from ImranR98/dev
- Bugfix: Include GitLab token in APK request (#1622)
- Bugfix: Don't trim trailing slashes when parsing links (#1625)
- Improve contrast of placeholder icon in dark mode (#1637) 
- Improve app loading times
- Revert a previous change to background downloads
- Add a "clear logs" button
2024-05-24 16:27:18 -04:00
Imran Remtulla
6a73ade359 Add a "clear logs" button 2024-05-23 21:18:13 -04:00
Imran Remtulla
6c5e5043a4 Improve icon loading after last commit 2024-05-23 21:02:50 -04:00
Imran Remtulla
5edaf1306d Update packages + Flutter, increment version 2024-05-23 20:17:44 -04:00
Imran Remtulla
5bf7fdb94e Revert a previous change related to BG downloads (did not work as intended) 2024-05-23 20:15:25 -04:00
Imran Remtulla
7808bc5ccb Improve loading time/stability (at the cost of icon flickering) 2024-05-23 20:02:43 -04:00
Imran Remtulla
06a079e452 Attempt to improve load times again + link parsing bugfix (#1625) 2024-05-23 19:35:52 -04:00
Imran Remtulla
de509737e6 Include GitLab token in APK request (#1622) 2024-05-22 19:51:35 -04:00
Imran Remtulla
08a3ba8d13 Merge remote-tracking branch 'origin/main' into dev 2024-05-22 19:37:30 -04:00
Imran
2b27902d5f Merge pull request #1633 from teaminh/main
Update vi.json
2024-05-22 19:37:16 -04:00
Imran Remtulla
62185127c2 Merge remote-tracking branch 'origin/main' into dev 2024-05-22 19:36:53 -04:00
Imran Remtulla
9c46e3f88c Attempt to improve app load time
Slight icon opacity tweak (#1637)
2024-05-22 19:35:51 -04:00
teaminh
daa4de921d Update vi.json 2024-05-19 12:20:28 +07:00
teaminh
bc977e2a5a Update vi.json 2024-05-19 12:09:36 +07:00
Imran
1e3815ca20 Merge pull request #1631 from ImranR98/dev
- Don't use partial downloads for BG tasks (more reliable)
- More accurate error reports for Huawei AppGallery fails
- Various bugfixes
- Update Flutter
2024-05-17 22:35:12 -04:00
Imran Remtulla
0e2fa96b9f More accurate error reports for Huawei AppGallery fails 2024-05-17 16:17:51 -04:00
Imran Remtulla
389aebe54e Various bugfixes 2024-05-17 15:56:41 -04:00
Imran Remtulla
fbfeaf2a91 Update Flutter, packages, increment version 2024-05-16 22:31:21 -04:00
Imran Remtulla
485812d076 Merge remote-tracking branch 'origin/main' into dev 2024-05-16 22:21:58 -04:00
Imran Remtulla
68e98ec719 Don't use partial downloads for BG tasks (more reliable) 2024-05-16 22:21:52 -04:00
Imran
cbe41de734 Merge pull request #1620 from mxhdee/main
Update fa.json
2024-05-16 22:11:23 -04:00
Mxhdee
abb8641105 Update fa.json 2024-05-13 18:46:01 -04:00
Imran
dbcb4b3c09 Merge pull request #1619 from ImranR98/dev
- Bugfix: Get initial data on config import
- Bugfix: Include overrideSource in exported config
2024-05-12 20:47:51 -04:00
Imran Remtulla
b231c756e6 Increment version, update packages 2024-05-12 20:47:06 -04:00
Imran Remtulla
3cb3f7fdd4 Merge remote-tracking branch 'origin/main' into dev 2024-05-12 20:45:34 -04:00
Imran
9837e8e325 Merge pull request #1608 from ArcticFoxPro/main
Update zh.json
2024-05-12 20:45:21 -04:00
Imran
73d4814f18 Merge pull request #1616 from KoolTechTricks/patch-1
Update ru.json
2024-05-12 20:45:04 -04:00
Imran
0a9219c314 Merge pull request #1618 from GitGitro/main
Enable per-app language, closes #1430
2024-05-12 20:44:55 -04:00
Imran Remtulla
56c5a73d9a Bugfix: Get initial data on config import 2024-05-12 20:41:53 -04:00
Imran Remtulla
a30e063246 Bugfix: Include overrideSource in exported config 2024-05-12 20:12:02 -04:00
GitGitro
bd26b6514a Enable per-app language, closes #1430 2024-05-12 18:07:35 +02:00
Kool Tech Tricks
3ea8c7e888 Update ru.json
Fix incorrect machine translated strings
2024-05-11 21:26:42 +05:00
有鲫雪狐
5f2ec5ce6f Update zh.json 2024-05-07 08:12:17 +08:00
ArcticFoxPro
783ce9d555 Update zh.json
`trackOnlyInBrackets`、`searchableInBrackets`:更正半角标点为全角
`enableBackgroundUpdates`:改为“启用全局后台更新”突出全局性
`exemptFromBackgroundUpdates`:“仅此应用生效,即使已启用全局后台更新”更明确了选项条件
`shizukuPretendToBeGooglePlay`:Google Play 不是确定的字符串,在这里指代了 Play Store 实体,无需双引号。
`useLatestAssetDateAsReleaseDate`:英文 Latest 通常在简体中文对应“最新”而非“最近”。
2024-05-06 19:12:38 +08:00
ArcticFoxPro
a719b2475b Update zh.json 2024-05-06 13:21:08 +08:00
19 changed files with 434 additions and 312 deletions

View File

@@ -6,7 +6,8 @@
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true"
android:localeConfig="@xml/locales_config">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="bs"/>
<locale android:name="cs"/>
<locale android:name="de"/>
<locale android:name="en"/>
<locale android:name="es"/>
<locale android:name="fa"/>
<locale android:name="fr"/>
<locale android:name="hu"/>
<locale android:name="it"/>
<locale android:name="ja"/>
<locale android:name="nl"/>
<locale android:name="pl"/>
<locale android:name="pt"/>
<locale android:name="ru"/>
<locale android:name="sv"/>
<locale android:name="tr"/>
<locale android:name="uk"/>
<locale android:name="vi"/>
<locale android:name="zh"/>
</locale-config>

View File

@@ -113,7 +113,7 @@
"dark": "تاریک", "dark": "تاریک",
"light": "روشن", "light": "روشن",
"followSystem": "هماهنگ با سیستم", "followSystem": "هماهنگ با سیستم",
"followSystemThemeExplanation": "Following system theme is possible only by using third-party applications", "followSystemThemeExplanation": "دنبال کردن تم سیستم فقط با استفاده از برنامه های شخص ثالث امکان پذیر است",
"useBlackTheme": "استفاده از تم تیره سیاه خالص", "useBlackTheme": "استفاده از تم تیره سیاه خالص",
"appSortBy": "مرتب سازی برنامه بر اساس", "appSortBy": "مرتب سازی برنامه بر اساس",
"authorName": "سازنده/اسم", "authorName": "سازنده/اسم",
@@ -147,10 +147,10 @@
"noNewUpdates": "به روز رسانی جدیدی وجود ندارد.", "noNewUpdates": "به روز رسانی جدیدی وجود ندارد.",
"xHasAnUpdate": "{} یک به روز رسانی دارد.", "xHasAnUpdate": "{} یک به روز رسانی دارد.",
"appsUpdated": "برنامه ها به روز شدند", "appsUpdated": "برنامه ها به روز شدند",
"appsNotUpdated": "Failed to update applications", "appsNotUpdated": "به روز رسانی برنامه ها ناموفق بود",
"appsUpdatedNotifDescription": "به کاربر اطلاع می دهد که به روز رسانی یک یا چند برنامه در پس زمینه اعمال شده است", "appsUpdatedNotifDescription": "به کاربر اطلاع می دهد که به روز رسانی یک یا چند برنامه در پس زمینه اعمال شده است",
"xWasUpdatedToY": "{} به {} به روز شد.", "xWasUpdatedToY": "{} به {} به روز شد.",
"xWasNotUpdatedToY": "Failed to update {} to {}.", "xWasNotUpdatedToY": "به روز رسانی {} به {} انجام نشد.",
"errorCheckingUpdates": "خطا در بررسی به‌روزرسانی‌ها", "errorCheckingUpdates": "خطا در بررسی به‌روزرسانی‌ها",
"errorCheckingUpdatesNotifDescription": "اعلانی که وقتی بررسی به‌روزرسانی پس‌زمینه ناموفق است نشان می‌دهد", "errorCheckingUpdatesNotifDescription": "اعلانی که وقتی بررسی به‌روزرسانی پس‌زمینه ناموفق است نشان می‌دهد",
"appsRemoved": "برنامه ها حذف شدند", "appsRemoved": "برنامه ها حذف شدند",
@@ -189,9 +189,9 @@
"disableVersionDetection": "غیرفعال کردن تشخیص نسخه", "disableVersionDetection": "غیرفعال کردن تشخیص نسخه",
"noVersionDetectionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند.", "noVersionDetectionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند.",
"downloadingX": "در حال دانلود {}", "downloadingX": "در حال دانلود {}",
"downloadX": "Download {}", "downloadX": "دانلود {}",
"downloadedX": "Downloaded {}", "downloadedX": "دانلود شده {}",
"releaseAsset": "Release Asset", "releaseAsset": "انتشار دارایی",
"downloadNotifDescription": "کاربر را از پیشرفت دانلود یک برنامه مطلع می کند", "downloadNotifDescription": "کاربر را از پیشرفت دانلود یک برنامه مطلع می کند",
"noAPKFound": "APK پیدا نشد فایل", "noAPKFound": "APK پیدا نشد فایل",
"noVersionDetection": "بدون تشخیص نسخه", "noVersionDetection": "بدون تشخیص نسخه",
@@ -305,13 +305,13 @@
"installed": "نصب شده است", "installed": "نصب شده است",
"latest": "آخرین", "latest": "آخرین",
"invertRegEx": "معکوس کردن عبارت منظم", "invertRegEx": "معکوس کردن عبارت منظم",
"note": "Note", "note": "یادداشت",
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.", "selfHostedNote": "از منوی کرکره ای \"{}\" می توان برای دسترسی به نمونه های خود میزبانی/سفارشی از هر منبعی استفاده کرد.",
"badDownload": "The APK could not be parsed (incompatible or partial download)", "badDownload": "APK قابل تجزیه نیست (دانلود ناسازگار یا جزئی)",
"beforeNewInstallsShareToAppVerifier": "Share new Apps with AppVerifier (if available)", "beforeNewInstallsShareToAppVerifier": "اشتراک‌گذاری برنامه‌های جدید با AppVerifier (در صورت وجود)",
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.", "appVerifierInstructionToast": "در AppVerifier به اشتراک بگذارید، سپس پس از آماده شدن به اینجا برگردید.",
"wiki": "Help/Wiki", "wiki": "راهنما/ویکی",
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (use at your own risk)", "crowdsourcedConfigsLabel": "تنظیمات برنامه Crowdsourced (با مسئولیت خود استفاده کنید)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "برنامه حذف شود؟", "one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟" "other": "برنامه ها حذف شوند؟"
@@ -361,8 +361,8 @@
"other": "{} و {} برنامه دیگر به روز شدند." "other": "{} و {} برنامه دیگر به روز شدند."
}, },
"xAndNMoreUpdatesFailed": { "xAndNMoreUpdatesFailed": {
"one": "Failed to update {} and 1 more app.", "one": "{} و 1 برنامه دیگر به روز نشد.",
"other": "Failed to update {} and {} more apps." "other": "{} و {} برنامه دیگر به روز نشد."
}, },
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.", "one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.",

View File

@@ -9,7 +9,7 @@
"placeholder": "Заполнитель", "placeholder": "Заполнитель",
"someErrors": "Возникли некоторые ошибки", "someErrors": "Возникли некоторые ошибки",
"unexpectedError": "Неожиданная ошибка", "unexpectedError": "Неожиданная ошибка",
"ok": "Ok", "ok": "Ок",
"and": "и", "and": "и",
"githubPATLabel": "Персональный токен доступа GitHub\n(увеличивает лимит запросов)", "githubPATLabel": "Персональный токен доступа GitHub\n(увеличивает лимит запросов)",
"includePrereleases": "Включить предварительные релизы", "includePrereleases": "Включить предварительные релизы",
@@ -135,7 +135,7 @@
"close": "Закрыть", "close": "Закрыть",
"share": "Поделиться", "share": "Поделиться",
"appNotFound": "Приложение не найдено", "appNotFound": "Приложение не найдено",
"obtainiumExportHyphenatedLowercase": "получение-экспорт", "obtainiumExportHyphenatedLowercase": "экспорт-obtainium",
"pickAnAPK": "Выберите APK-файл", "pickAnAPK": "Выберите APK-файл",
"appHasMoreThanOnePackage": "{} имеет более одного пакета:", "appHasMoreThanOnePackage": "{} имеет более одного пакета:",
"deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}", "deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}",
@@ -179,8 +179,8 @@
"appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено", "appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено",
"reposHaveMultipleApps": "В хранилище несколько приложений", "reposHaveMultipleApps": "В хранилище несколько приложений",
"fdroidThirdPartyRepo": "Сторонние репозитории F-Droid", "fdroidThirdPartyRepo": "Сторонние репозитории F-Droid",
"steamMobile": "Стим Мобайл", "steamMobile": "Приложение Steam",
"steamChat": "Стим-чат", "steamChat": "Steam Chat",
"install": "Установить", "install": "Установить",
"markInstalled": "Пометить как установленное", "markInstalled": "Пометить как установленное",
"update": "Обновить", "update": "Обновить",
@@ -191,7 +191,7 @@
"downloadingX": "Загрузка {}", "downloadingX": "Загрузка {}",
"downloadX": "Скачать {}", "downloadX": "Скачать {}",
"downloadedX": "Загружено {}", "downloadedX": "Загружено {}",
"releaseAsset": "Освобождение актива", "releaseAsset": "Релизный объект",
"downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения", "downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения",
"noAPKFound": "APK не найден", "noAPKFound": "APK не найден",
"noVersionDetection": "Обнаружение версий отключено", "noVersionDetection": "Обнаружение версий отключено",
@@ -254,7 +254,7 @@
"intermediateLinkRegex": "Фильтр для \"промежуточной\" ссылки для посещения", "intermediateLinkRegex": "Фильтр для \"промежуточной\" ссылки для посещения",
"filterByLinkText": "Фильтрация ссылок по тексту ссылки", "filterByLinkText": "Фильтрация ссылок по тексту ссылки",
"intermediateLinkNotFound": "Промежуточная ссылка не найдена", "intermediateLinkNotFound": "Промежуточная ссылка не найдена",
"intermediateLink": "Промежуточное звено", "intermediateLink": "Промежуточная ссылка",
"exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)", "exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)",
"bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет соединения с Wi-Fi", "bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет соединения с Wi-Fi",
"autoSelectHighestVersionCode": "Автоматически выбирать APK с актуальной версией кода", "autoSelectHighestVersionCode": "Автоматически выбирать APK с актуальной версией кода",

View File

@@ -22,9 +22,9 @@
"requiredInBrackets": "(Yêu cầu)", "requiredInBrackets": "(Yêu cầu)",
"dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN", "dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN",
"colour": "Màu sắc", "colour": "Màu sắc",
"standard": "Standard", "standard": "Mặc định",
"custom": "Custom", "custom": "Tùy chỉnh",
"useMaterialYou": "Use Material You", "useMaterialYou": "Sử dụng Material You",
"githubStarredRepos": "Kho lưu trữ có gắn dấu sao GitHub", "githubStarredRepos": "Kho lưu trữ có gắn dấu sao GitHub",
"uname": "Tên người dùng", "uname": "Tên người dùng",
"wrongArgNum": "Số lượng đối số được cung cấp sai", "wrongArgNum": "Số lượng đối số được cung cấp sai",
@@ -147,10 +147,10 @@
"noNewUpdates": "Không có bản cập nhật mới.", "noNewUpdates": "Không có bản cập nhật mới.",
"xHasAnUpdate": "{} có bản cập nhật.", "xHasAnUpdate": "{} có bản cập nhật.",
"appsUpdated": "Ứng dụng đã cập nhật ", "appsUpdated": "Ứng dụng đã cập nhật ",
"appsNotUpdated": "Failed to update applications", "appsNotUpdated": "Ứng dụng đã cập nhật không thành công",
"appsUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng đã được áp dụng trong nền", "appsUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng đã được áp dụng trong nền",
"xWasUpdatedToY": "{} đã được cập nhật thành {}.", "xWasUpdatedToY": "{} đã được cập nhật thành {}.",
"xWasNotUpdatedToY": "Failed to update {} to {}.", "xWasNotUpdatedToY": "{} đã cập nhật thành {} không thành công.",
"errorCheckingUpdates": "Lỗi kiểm tra bản cập nhật", "errorCheckingUpdates": "Lỗi kiểm tra bản cập nhật",
"errorCheckingUpdatesNotifDescription": "Thông báo hiển thị khi kiểm tra cập nhật nền không thành công", "errorCheckingUpdatesNotifDescription": "Thông báo hiển thị khi kiểm tra cập nhật nền không thành công",
"appsRemoved": "Ứng dụng đã loại bỏ", "appsRemoved": "Ứng dụng đã loại bỏ",
@@ -189,8 +189,8 @@
"disableVersionDetection": "Tắt tính năng phát hiện phiên bản", "disableVersionDetection": "Tắt tính năng phát hiện phiên bản",
"noVersionDetectionExplanation": "Chỉ nên sử dụng tùy chọn này cho Ứng dụng mà tính năng phát hiện phiên bản không hoạt động chính xác.", "noVersionDetectionExplanation": "Chỉ nên sử dụng tùy chọn này cho Ứng dụng mà tính năng phát hiện phiên bản không hoạt động chính xác.",
"downloadingX": "Đang tải xuống {}", "downloadingX": "Đang tải xuống {}",
"downloadX": "Download {}", "downloadX": "Tải xuống {}",
"downloadedX": "Downloaded {}", "downloadedX": "Đã tải xuống {}",
"releaseAsset": "Release Asset", "releaseAsset": "Release Asset",
"downloadNotifDescription": "Thông báo cho người dùng về tiến trình tải xuống Ứng dụng", "downloadNotifDescription": "Thông báo cho người dùng về tiến trình tải xuống Ứng dụng",
"noAPKFound": "Không tìm thấy APK", "noAPKFound": "Không tìm thấy APK",
@@ -288,10 +288,10 @@
"supportFixedAPKURL": "Hỗ trợ URL APK cố định", "supportFixedAPKURL": "Hỗ trợ URL APK cố định",
"selectX": "Lựa chọn {}", "selectX": "Lựa chọn {}",
"parallelDownloads": "Cho phép tải đa luồng", "parallelDownloads": "Cho phép tải đa luồng",
"useShizuku": "Use Shizuku or Sui to install", "useShizuku": "Sử dụng Shizuku hoặc Sui để cài đặt",
"shizukuBinderNotFound": "Shizuku chưa khởi động", "shizukuBinderNotFound": "Shizuku chưa khởi động",
"shizukuOld": "Old Shizuku version (<11) - update it", "shizukuOld": "Phiên bản Shizuku lỗi thời (<11) - hãy cập nhật nó",
"shizukuOldAndroidWithADB": "Shizuku running on Android < 8.1 with ADB - update Android or use Sui instead", "shizukuOldAndroidWithADB": "Shizuku chạy trên Android < 8.1 với ADB - hãy cập nhật Android hoặc thay bằng Sui",
"shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)", "shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)",
"useSystemFont": "Sử dụng phông chữ hệ thống", "useSystemFont": "Sử dụng phông chữ hệ thống",
"useVersionCodeAsOSVersion": "Sử dụng Mã phiên bản ứng dụng làm phiên bản do hệ điều hành phát hiện", "useVersionCodeAsOSVersion": "Sử dụng Mã phiên bản ứng dụng làm phiên bản do hệ điều hành phát hiện",
@@ -310,7 +310,7 @@
"badDownload": "Không thể phân tích cú pháp APK (tải xuống một phần hoặc không tương thích)", "badDownload": "Không thể phân tích cú pháp APK (tải xuống một phần hoặc không tương thích)",
"beforeNewInstallsShareToAppVerifier": "Chia sẻ ứng dụng mới với AppVerifier (nếu có)", "beforeNewInstallsShareToAppVerifier": "Chia sẻ ứng dụng mới với AppVerifier (nếu có)",
"appVerifierInstructionToast": "Chia sẻ lên AppVerifier, sau đó quay lại đây khi sẵn sàng.", "appVerifierInstructionToast": "Chia sẻ lên AppVerifier, sau đó quay lại đây khi sẵn sàng.",
"wiki": "Help/Wiki", "wiki": "Trợ giúp/Wiki",
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (use at your own risk)", "crowdsourcedConfigsLabel": "Crowdsourced App Configurations (use at your own risk)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Gỡ ứng dụng?", "one": "Gỡ ứng dụng?",
@@ -361,8 +361,8 @@
"other": "{} và {} ứng dụng khác đã được cập nhật." "other": "{} và {} ứng dụng khác đã được cập nhật."
}, },
"xAndNMoreUpdatesFailed": { "xAndNMoreUpdatesFailed": {
"one": "Failed to update {} and 1 more app.", "one": "{} và 1 ứng dụng khác đã cập nhật không thành công.",
"other": "Failed to update {} and {} more apps." "other": "{} và {} ứng dụng khác đã cập nhật không thảnh công."
}, },
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} và 1 ứng dụng khác có thể đã được cập nhật.", "one": "{} và 1 ứng dụng khác có thể đã được cập nhật.",

View File

@@ -24,7 +24,7 @@
"colour": "配色", "colour": "配色",
"standard": "标准", "standard": "标准",
"custom": "定制", "custom": "定制",
"useMaterialYou": "使用 Material You 配色", "useMaterialYou": "使用 Material You",
"githubStarredRepos": "已星标的 GitHub 仓库", "githubStarredRepos": "已星标的 GitHub 仓库",
"uname": "用户名", "uname": "用户名",
"wrongArgNum": "参数数量错误", "wrongArgNum": "参数数量错误",
@@ -45,8 +45,8 @@
"search": "搜索", "search": "搜索",
"additionalOptsFor": "{} 的更多选项", "additionalOptsFor": "{} 的更多选项",
"supportedSources": "支持的来源", "supportedSources": "支持的来源",
"trackOnlyInBrackets": "(仅追踪)", "trackOnlyInBrackets": "仅追踪",
"searchableInBrackets": "(可搜索)", "searchableInBrackets": "可搜索",
"appsString": "应用列表", "appsString": "应用列表",
"noApps": "无应用", "noApps": "无应用",
"noAppsForFilter": "没有符合条件的应用", "noAppsForFilter": "没有符合条件的应用",
@@ -125,7 +125,7 @@
"bgUpdateCheckInterval": "后台更新检查间隔", "bgUpdateCheckInterval": "后台更新检查间隔",
"neverManualOnly": "手动", "neverManualOnly": "手动",
"appearance": "外观", "appearance": "外观",
"showWebInAppView": "应用详情页显示来源网", "showWebInAppView": "应用详情页显示来源网站内容",
"pinUpdates": "将待更新应用置顶", "pinUpdates": "将待更新应用置顶",
"updates": "更新", "updates": "更新",
"sourceSpecific": "来源", "sourceSpecific": "来源",
@@ -147,7 +147,7 @@
"noNewUpdates": "全部应用已是最新。", "noNewUpdates": "全部应用已是最新。",
"xHasAnUpdate": "“{}”可以更新了。", "xHasAnUpdate": "“{}”可以更新了。",
"appsUpdated": "应用已更新", "appsUpdated": "应用已更新",
"appsNotUpdated": "更新应用程序失败", "appsNotUpdated": "更新应用失败",
"appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知", "appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知",
"xWasUpdatedToY": "“{}”已更新至 {}。", "xWasUpdatedToY": "“{}”已更新至 {}。",
"xWasNotUpdatedToY": "未能将 {} 更新为 {}。", "xWasNotUpdatedToY": "未能将 {} 更新为 {}。",
@@ -191,7 +191,7 @@
"downloadingX": "正在下载“{}”", "downloadingX": "正在下载“{}”",
"downloadX": "下载 {}", "downloadX": "下载 {}",
"downloadedX": "下载 {}", "downloadedX": "下载 {}",
"releaseAsset": "APK 文件", "releaseAsset": "发行版附件",
"downloadNotifDescription": "提示应用的下载进度", "downloadNotifDescription": "提示应用的下载进度",
"noAPKFound": "未找到 APK 文件", "noAPKFound": "未找到 APK 文件",
"noVersionDetection": "禁用版本检测", "noVersionDetection": "禁用版本检测",
@@ -201,7 +201,7 @@
"noCategory": "无类别", "noCategory": "无类别",
"noCategories": "无类别", "noCategories": "无类别",
"deleteCategoriesQuestion": "是否删除选中的类别?", "deleteCategoriesQuestion": "是否删除选中的类别?",
"categoryDeleteWarning": "被删除类别的应用将恢复为未分类状态。", "categoryDeleteWarning": "被删除类别的应用将恢复为未分类状态。",
"addCategory": "添加类别", "addCategory": "添加类别",
"label": "标签", "label": "标签",
"language": "语言", "language": "语言",
@@ -247,7 +247,7 @@
"appsPossiblyUpdated": "已尝试更新应用", "appsPossiblyUpdated": "已尝试更新应用",
"appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知", "appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知",
"xWasPossiblyUpdatedToY": "已尝试将“{}”更新至 {}。", "xWasPossiblyUpdatedToY": "已尝试将“{}”更新至 {}。",
"enableBackgroundUpdates": "启用后台更新", "enableBackgroundUpdates": "启用全局后台更新",
"backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。", "backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。",
"backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。", "backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。",
"verifyLatestTag": "验证“Latest”标签", "verifyLatestTag": "验证“Latest”标签",
@@ -255,12 +255,12 @@
"filterByLinkText": "根据链接文本进行筛选", "filterByLinkText": "根据链接文本进行筛选",
"intermediateLinkNotFound": "未找到中转链接", "intermediateLinkNotFound": "未找到中转链接",
"intermediateLink": "中转链接", "intermediateLink": "中转链接",
"exemptFromBackgroundUpdates": "禁用后台更新(如果已经全局启用", "exemptFromBackgroundUpdates": "禁用后台更新(仅此应用生效,即使已启用全局后台更新",
"bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新", "bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
"autoSelectHighestVersionCode": "自动选择内部版本号最高的 APK 文件", "autoSelectHighestVersionCode": "自动选择内部版本号最高的 APK 文件",
"versionExtractionRegEx": "提取版本号的正则表达式", "versionExtractionRegEx": "提取版本号的正则表达式",
"matchGroupToUse": "从上述匹配结果中引用的捕获组", "matchGroupToUse": "从上述匹配结果中引用的捕获组",
"highlightTouchTargets": "突出展示不明显的触摸区域", "highlightTouchTargets": "突出展示不明显的可交互区域",
"pickExportDir": "选择导出文件夹", "pickExportDir": "选择导出文件夹",
"autoExportOnChanges": "数据变更时自动导出", "autoExportOnChanges": "数据变更时自动导出",
"includeSettings": "同时导出应用设置", "includeSettings": "同时导出应用设置",
@@ -291,12 +291,12 @@
"useShizuku": "使用 Shizuku 或 Sui 安装", "useShizuku": "使用 Shizuku 或 Sui 安装",
"shizukuBinderNotFound": "未发现兼容的 Shizuku 服务", "shizukuBinderNotFound": "未发现兼容的 Shizuku 服务",
"shizukuOld": "Shizuku 版本过低(<11- 请更新", "shizukuOld": "Shizuku 版本过低(<11- 请更新",
"shizukuOldAndroidWithADB": "正在低版本 Android<8.1)系统中以 ADB 模式运行 Shizuku - 请更新 Android 版本或使用 Sui 代替", "shizukuOldAndroidWithADB": "正在低版本 Android<8.1)系统中以 ADB 模式运行 Shizuku - 请更新 Android 系统版本或使用 Sui 代替",
"shizukuPretendToBeGooglePlay": "使用 Shizuku 时,将安装来源伪装为Google Play", "shizukuPretendToBeGooglePlay": "将安装来源伪装为 Google Play(需要使用 Shizuku",
"useSystemFont": "使用系统字体", "useSystemFont": "使用系统字体",
"useVersionCodeAsOSVersion": "使用内部版本号代替应用定义的版本号", "useVersionCodeAsOSVersion": "使用内部版本号代替应用定义的版本号",
"requestHeader": "请求标头", "requestHeader": "请求标头",
"useLatestAssetDateAsReleaseDate": "使用最文件上传时间作为发行日期", "useLatestAssetDateAsReleaseDate": "使用最文件上传时间作为发行日期",
"defaultPseudoVersioningMethod": "默认虚拟版本方案", "defaultPseudoVersioningMethod": "默认虚拟版本方案",
"partialAPKHash": "APK 文件散列值片段", "partialAPKHash": "APK 文件散列值片段",
"APKLinkHash": "APK 文件链接散列值", "APKLinkHash": "APK 文件链接散列值",
@@ -310,19 +310,19 @@
"badDownload": "无法解析 APK 文件(不兼容或文件不完整)", "badDownload": "无法解析 APK 文件(不兼容或文件不完整)",
"beforeNewInstallsShareToAppVerifier": "通过 AppVerifier 校验新应用(如果可用)", "beforeNewInstallsShareToAppVerifier": "通过 AppVerifier 校验新应用(如果可用)",
"appVerifierInstructionToast": "分享至 AppVerifier完成后返回此处。", "appVerifierInstructionToast": "分享至 AppVerifier完成后返回此处。",
"wiki": "帮助/维基", "wiki": "帮助/Wiki",
"crowdsourcedConfigsLabel": "众包应用程序配置(使用风险自负)", "crowdsourcedConfigsLabel": "众包应用程序配置(使用风险自负)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "是否删除应用?", "one": "是否删除应用?",
"other": "是否删除应用?" "other": "是否删除应用?"
}, },
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "API 请求过于频繁(速率限制)- 在 {} 分钟后重试", "one": "API 请求过于频繁(速率限制)- 在 {} 分钟后重试",
"other": "API 请求过于频繁(速率限制)- 在 {} 分钟后重试" "other": "API 请求过于频繁(速率限制)- 在 {} 分钟后重试"
}, },
"bgUpdateGotErrorRetryInMinutes": { "bgUpdateGotErrorRetryInMinutes": {
"one": "后台更新检查遇到了“{}”问题,预定于 {} 分钟后重试", "one": "后台更新检查遇到了“{}”问题,于 {} 分钟后重试",
"other": "后台更新检查遇到了“{}”问题,预定于 {} 分钟后重试" "other": "后台更新检查遇到了“{}”问题,于 {} 分钟后重试"
}, },
"bgCheckFoundUpdatesWillNotifyIfNeeded": { "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "后台检查发现 {} 个应用更新 - 如有需要将发送通知", "one": "后台检查发现 {} 个应用更新 - 如有需要将发送通知",
@@ -361,8 +361,8 @@
"other": "“{}”和另外 {} 个应用已更新。" "other": "“{}”和另外 {} 个应用已更新。"
}, },
"xAndNMoreUpdatesFailed": { "xAndNMoreUpdatesFailed": {
"one": "更新 {} 和另外 1 个应用程序失败。", "one": "{} 和另外 1 个应用更新失败。",
"other": "未能更新 {} 和 {} 更多应用程序。" "other": "{} 和另外 {} 个应用更新失败。"
}, },
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} 和另外 1 个应用已尝试更新。", "one": "{} 和另外 1 个应用已尝试更新。",

View File

@@ -111,6 +111,14 @@ class GitLab extends AppSource {
} }
} }
@override
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
Map<String, dynamic> additionalSettings) async {
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
String optionalAuth = (PAT != null) ? 'private_token=$PAT' : '';
return '$apkUrl?$optionalAuth';
}
@override @override
Future<APKDetails> getLatestAPKDetails( Future<APKDetails> getLatestAPKDetails(
String standardUrl, String standardUrl,

View File

@@ -73,21 +73,23 @@ class HuaweiAppGallery extends AppSource {
throw NoReleasesError(); throw NoReleasesError();
} }
String appId = appIdFromRedirectDlUrl(res.headers['location']!); String appId = appIdFromRedirectDlUrl(res.headers['location']!);
if (appId.isEmpty) {
throw NoReleasesError();
}
var relDateStr = var relDateStr =
res.headers['location']?.split('?')[0].split('.').reversed.toList()[1]; res.headers['location']?.split('?')[0].split('.').reversed.toList()[1];
var relDateStrAdj = relDateStr?.split(''); if (relDateStr == null || relDateStr.length != 10) {
var tempLen = relDateStrAdj?.length ?? 0;
var i = 2;
while (i < tempLen) {
relDateStrAdj?.insert((i + i ~/ 2 - 1), '-');
i += 2;
}
var relDate = relDateStrAdj == null
? null
: DateFormat('yy-MM-dd-HH-mm', 'en_US').parse(relDateStrAdj.join(''));
if (relDateStr == null) {
throw NoVersionError(); throw NoVersionError();
} }
var relDateStrAdj = relDateStr.split('');
var tempLen = relDateStrAdj.length;
var i = 2;
while (i < tempLen) {
relDateStrAdj.insert((i + i ~/ 2 - 1), '-');
i += 2;
}
var relDate =
DateFormat('yy-MM-dd-HH-mm', 'en_US').parse(relDateStrAdj.join(''));
return APKDetails( return APKDetails(
relDateStr, [MapEntry('$appId.apk', dlUrl)], AppNames(name, appId), relDateStr, [MapEntry('$appId.apk', dlUrl)], AppNames(name, appId),
releaseDate: relDate); releaseDate: relDate);

View File

@@ -224,7 +224,7 @@ class _ObtainiumState extends State<Obtainium> {
// set the background and surface colors to pure black in the amoled theme // set the background and surface colors to pure black in the amoled theme
if (settingsProvider.useBlackTheme) { if (settingsProvider.useBlackTheme) {
darkColorScheme = darkColorScheme darkColorScheme = darkColorScheme
.copyWith(background: Colors.black, surface: Colors.black) .copyWith(surface: Colors.black)
.harmonized(); .harmonized();
} }

View File

@@ -226,18 +226,26 @@ class _AppPageState extends State<AppPage> {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const SizedBox(height: 20), const SizedBox(height: 20),
app?.icon != null FutureBuilder(
? Row(mainAxisAlignment: MainAxisAlignment.center, children: [ future: appsProvider.updateAppIcon(app?.app.id),
GestureDetector( builder: (ctx, val) {
child: Image.memory( return app?.icon != null
app!.icon!, ? Row(
height: 150, mainAxisAlignment: MainAxisAlignment.center,
gaplessPlayback: true, children: [
), GestureDetector(
onTap: () => pm.openApp(app.app.id), onTap: app == null
) ? null
]) : () => pm.openApp(app.app.id),
: Container(), child: Image.memory(
app!.icon!,
height: 150,
gaplessPlayback: true,
),
)
])
: Container();
}),
const SizedBox( const SizedBox(
height: 25, height: 25,
), ),
@@ -286,7 +294,7 @@ class _AppPageState extends State<AppPage> {
? WebViewWidget( ? WebViewWidget(
controller: WebViewController() controller: WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted) ..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(Theme.of(context).colorScheme.background) ..setBackgroundColor(Theme.of(context).colorScheme.surface)
..setJavaScriptMode(JavaScriptMode.unrestricted) ..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate( ..setNavigationDelegate(
NavigationDelegate( NavigationDelegate(

View File

@@ -354,7 +354,11 @@ class AppsPageState extends State<AppsPage> {
SliverFillRemaining( SliverFillRemaining(
child: Center( child: Center(
child: Text( child: Text(
appsProvider.apps.isEmpty ? tr('noApps') : tr('noAppsForFilter'), appsProvider.apps.isEmpty
? appsProvider.loadingApps
? tr('pleaseWait')
: tr('noApps')
: tr('noAppsForFilter'),
style: Theme.of(context).textTheme.headlineMedium, style: Theme.of(context).textTheme.headlineMedium,
textAlign: TextAlign.center, textAlign: TextAlign.center,
))), ))),
@@ -402,29 +406,36 @@ class AppsPageState extends State<AppsPage> {
} }
getAppIcon(int appIndex) { getAppIcon(int appIndex) {
return listedApps[appIndex].icon != null return FutureBuilder(
? Image.memory( future: appsProvider.updateAppIcon(listedApps[appIndex].app.id),
listedApps[appIndex].icon!, builder: (ctx, val) {
gaplessPlayback: true, return listedApps[appIndex].icon != null
) ? Image.memory(
: Row( listedApps[appIndex].icon!,
mainAxisSize: MainAxisSize.min, gaplessPlayback: true,
mainAxisAlignment: MainAxisAlignment.center, )
children: [ : Row(
Transform( mainAxisSize: MainAxisSize.min,
alignment: Alignment.center, mainAxisAlignment: MainAxisAlignment.center,
transform: Matrix4.rotationZ(0.31), children: [
child: Padding( Transform(
padding: const EdgeInsets.all(15), alignment: Alignment.center,
child: Image( transform: Matrix4.rotationZ(0.31),
image: const AssetImage( child: Padding(
'assets/graphics/icon_small.png'), padding: const EdgeInsets.all(15),
color: Colors.white.withOpacity(0.3), child: Image(
colorBlendMode: BlendMode.modulate, image: const AssetImage(
gaplessPlayback: true, 'assets/graphics/icon_small.png'),
), color: Theme.of(context).brightness ==
)), Brightness.dark
]); ? Colors.white.withOpacity(0.4)
: Colors.white.withOpacity(0.3),
colorBlendMode: BlendMode.modulate,
gaplessPlayback: true,
),
)),
]);
});
} }
getVersionText(int appIndex) { getVersionText(int appIndex) {
@@ -503,7 +514,7 @@ class AppsPageState extends State<AppsPage> {
); );
var transparent = var transparent =
Theme.of(context).colorScheme.background.withAlpha(0).value; Theme.of(context).colorScheme.surface.withAlpha(0).value;
List<double> stops = [ List<double> stops = [
...listedApps[index].app.categories.asMap().entries.map( ...listedApps[index].app.categories.asMap().entries.map(
(e) => ((e.key / (listedApps[index].app.categories.length - 1)))), (e) => ((e.key / (listedApps[index].app.categories.length - 1)))),
@@ -893,7 +904,8 @@ class AppsPageState extends State<AppsPage> {
'preferredApkIndex': 'preferredApkIndex':
a.preferredApkIndex, a.preferredApkIndex,
'additionalSettings': 'additionalSettings':
jsonEncode(a.additionalSettings) jsonEncode(a.additionalSettings),
'overrideSource': a.overrideSource
}))}\n\n'; }))}\n\n';
} }
Share.share(urls, Share.share(urls,

View File

@@ -13,6 +13,7 @@ import 'package:obtainium/pages/import_export.dart';
import 'package:obtainium/pages/settings.dart'; import 'package:obtainium/pages/settings.dart';
import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/apps_provider.dart';
import 'package:obtainium/providers/settings_provider.dart'; import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {
@@ -102,13 +103,22 @@ class _HomePageState extends State<HomePage> {
}) != }) !=
null) { null) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
var result = await context.read<AppsProvider>().import( var appsProvider = context.read<AppsProvider>();
action == 'app' var result = await appsProvider.import(action == 'app'
? '{ "apps": [$dataStr] }' ? '{ "apps": [$dataStr] }'
: '{ "apps": $dataStr }'); : '{ "apps": $dataStr }');
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
showMessage( showMessage(
tr('importedX', args: [plural('apps', result.key)]), context); tr('importedX', args: [plural('apps', result.key.length)]),
context);
await appsProvider
.checkUpdates(specificIds: result.key.map((e) => e.id).toList())
.catchError((e) {
if (e is Map && e['errors'] is MultiAppMultiError) {
showError(e['errors'].toString(), context);
}
return <App>[];
});
} }
} else { } else {
throw ObtainiumError(tr('unknown')); throw ObtainiumError(tr('unknown'));

View File

@@ -33,7 +33,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
var settingsProvider = context.watch<SettingsProvider>(); var settingsProvider = context.watch<SettingsProvider>();
var outlineButtonStyle = ButtonStyle( var outlineButtonStyle = ButtonStyle(
shape: MaterialStateProperty.all( shape: WidgetStateProperty.all(
StadiumBorder( StadiumBorder(
side: BorderSide( side: BorderSide(
width: 1, width: 1,
@@ -144,7 +144,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
appsProvider.addMissingCategories(settingsProvider); appsProvider.addMissingCategories(settingsProvider);
showMessage( showMessage(
'${tr('importedX', args: [ '${tr('importedX', args: [
plural('apps', value.key) plural('apps', value.key.length)
])}${value.value ? ' + ${tr('settings')}' : ''}', ])}${value.value ? ' + ${tr('settings')}' : ''}',
context); context);
}); });

View File

@@ -5,6 +5,7 @@ import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/components/custom_app_bar.dart';
import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/components/generated_form_modal.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/main.dart'; import 'package:obtainium/main.dart';
import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/apps_provider.dart';
@@ -945,6 +946,25 @@ class _LogsDialogState extends State<LogsDialog> {
], ],
), ),
actions: [ actions: [
TextButton(
onPressed: () async {
var cont = (await showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext ctx) {
return GeneratedFormModal(
title: tr('appLogs'),
items: const [],
initValid: true,
message: tr('removeFromObtainium'),
);
})) !=
null;
if (cont) {
logsProvider.clear();
Navigator.of(context).pop();
}
},
child: Text(tr('remove'))),
TextButton( TextButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();

View File

@@ -329,6 +329,10 @@ Future<Map<String, String>> getHeaders(String url,
return returnHeaders; return returnHeaders;
} }
Future<List<PackageInfo>> getAllInstalledInfo() async {
return await pm.getInstalledPackages() ?? [];
}
Future<PackageInfo?> getInstalledInfo(String? packageName, Future<PackageInfo?> getInstalledInfo(String? packageName,
{bool printErr = true}) async { {bool printErr = true}) async {
if (packageName != null) { if (packageName != null) {
@@ -364,7 +368,9 @@ class AppsProvider with ChangeNotifier {
foregroundStream = FGBGEvents.stream.asBroadcastStream(); foregroundStream = FGBGEvents.stream.asBroadcastStream();
foregroundSubscription = foregroundStream?.listen((event) async { foregroundSubscription = foregroundStream?.listen((event) async {
isForeground = event == FGBGType.foreground; isForeground = event == FGBGType.foreground;
if (isForeground) loadApps(); if (isForeground) {
await loadApps();
}
}); });
() async { () async {
await settingsProvider.initializeSettings(); await settingsProvider.initializeSettings();
@@ -421,7 +427,8 @@ class AppsProvider with ChangeNotifier {
} }
Future<Object> downloadApp(App app, BuildContext? context, Future<Object> downloadApp(App app, BuildContext? context,
{NotificationsProvider? notificationsProvider}) async { {NotificationsProvider? notificationsProvider,
bool useExisting = true}) async {
var notifId = DownloadNotification(app.finalName, 0).id; var notifId = DownloadNotification(app.finalName, 0).id;
if (apps[app.id] != null) { if (apps[app.id] != null) {
apps[app.id]!.downloadProgress = 0; apps[app.id]!.downloadProgress = 0;
@@ -453,7 +460,7 @@ class AppsProvider with ChangeNotifier {
notificationsProvider?.notify(notif); notificationsProvider?.notify(notif);
} }
prevProg = prog; prevProg = prog;
}, APKDir.path); }, APKDir.path, useExisting: useExisting);
// Set to 90 for remaining steps, will make null in 'finally' // Set to 90 for remaining steps, will make null in 'finally'
if (apps[app.id] != null) { if (apps[app.id] != null) {
apps[app.id]!.downloadProgress = -1; apps[app.id]!.downloadProgress = -1;
@@ -766,7 +773,8 @@ class AppsProvider with ChangeNotifier {
Future<List<String>> downloadAndInstallLatestApps( Future<List<String>> downloadAndInstallLatestApps(
List<String> appIds, BuildContext? context, List<String> appIds, BuildContext? context,
{NotificationsProvider? notificationsProvider, {NotificationsProvider? notificationsProvider,
bool forceParallelDownloads = false}) async { bool forceParallelDownloads = false,
bool useExisting = true}) async {
notificationsProvider = notificationsProvider =
notificationsProvider ?? context?.read<NotificationsProvider>(); notificationsProvider ?? context?.read<NotificationsProvider>();
List<String> appsToInstall = []; List<String> appsToInstall = [];
@@ -818,21 +826,82 @@ class AppsProvider with ChangeNotifier {
appsToInstall = appsToInstall =
moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId); moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId);
Future<String> updateFn(String id, {bool skipInstalls = false}) async { Future<void> installFn(String id, bool willBeSilent,
DownloadedApk? downloadedFile, DownloadedXApkDir? downloadedDir) async {
apps[id]?.downloadProgress = -1;
notifyListeners();
try {
bool sayInstalled = true;
var contextIfNewInstall =
apps[id]?.installedInfo == null ? context : null;
bool needBGWorkaround =
willBeSilent && context == null && !settingsProvider.useShizuku;
if (downloadedFile != null) {
if (needBGWorkaround) {
// ignore: use_build_context_synchronously
installApk(downloadedFile, contextIfNewInstall,
needsBGWorkaround: true);
} else {
// ignore: use_build_context_synchronously
sayInstalled = await installApk(downloadedFile, contextIfNewInstall,
shizukuPretendToBeGooglePlay: apps[id]!
.app
.additionalSettings['shizukuPretendToBeGooglePlay'] ==
true);
}
} else {
if (needBGWorkaround) {
// ignore: use_build_context_synchronously
installXApkDir(downloadedDir!, contextIfNewInstall,
needsBGWorkaround: true);
} else {
// ignore: use_build_context_synchronously
sayInstalled = await installXApkDir(
downloadedDir!, contextIfNewInstall,
shizukuPretendToBeGooglePlay: apps[id]!
.app
.additionalSettings['shizukuPretendToBeGooglePlay'] ==
true);
}
}
if (willBeSilent && context == null) {
if (!settingsProvider.useShizuku) {
notificationsProvider?.notify(SilentUpdateAttemptNotification(
[apps[id]!.app],
id: id.hashCode));
} else {
notificationsProvider?.notify(SilentUpdateNotification(
[apps[id]!.app], sayInstalled,
id: id.hashCode));
}
}
if (sayInstalled) {
installedIds.add(id);
}
} finally {
apps[id]?.downloadProgress = null;
notifyListeners();
}
}
Future<Map<Object?, Object?>> downloadFn(String id,
{bool skipInstalls = false}) async {
bool willBeSilent = false;
DownloadedApk? downloadedFile;
DownloadedXApkDir? downloadedDir;
try { try {
var downloadedArtifact = var downloadedArtifact =
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
await downloadApp(apps[id]!.app, context, await downloadApp(apps[id]!.app, context,
notificationsProvider: notificationsProvider); notificationsProvider: notificationsProvider,
DownloadedApk? downloadedFile; useExisting: useExisting);
DownloadedXApkDir? downloadedDir;
if (downloadedArtifact is DownloadedApk) { if (downloadedArtifact is DownloadedApk) {
downloadedFile = downloadedArtifact; downloadedFile = downloadedArtifact;
} else { } else {
downloadedDir = downloadedArtifact as DownloadedXApkDir; downloadedDir = downloadedArtifact as DownloadedXApkDir;
} }
id = downloadedFile?.appId ?? downloadedDir!.appId; id = downloadedFile?.appId ?? downloadedDir!.appId;
bool willBeSilent = await canInstallSilently(apps[id]!.app); willBeSilent = await canInstallSilently(apps[id]!.app);
if (!settingsProvider.useShizuku) { if (!settingsProvider.useShizuku) {
if (!(await settingsProvider.getInstallPermission(enforce: false))) { if (!(await settingsProvider.getInstallPermission(enforce: false))) {
throw ObtainiumError(tr('cancelled')); throw ObtainiumError(tr('cancelled'));
@@ -853,80 +922,33 @@ class AppsProvider with ChangeNotifier {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
await waitForUserToReturnToForeground(context); await waitForUserToReturnToForeground(context);
} }
apps[id]?.downloadProgress = -1;
notifyListeners();
try {
if (!skipInstalls) {
bool sayInstalled = true;
var contextIfNewInstall =
apps[id]?.installedInfo == null ? context : null;
bool needBGWorkaround =
willBeSilent && context == null && !settingsProvider.useShizuku;
if (downloadedFile != null) {
if (needBGWorkaround) {
// ignore: use_build_context_synchronously
installApk(downloadedFile, contextIfNewInstall,
needsBGWorkaround: true);
} else {
// ignore: use_build_context_synchronously
sayInstalled = await installApk(
downloadedFile, contextIfNewInstall,
shizukuPretendToBeGooglePlay:
apps[id]!.app.additionalSettings[
'shizukuPretendToBeGooglePlay'] ==
true);
}
} else {
if (needBGWorkaround) {
// ignore: use_build_context_synchronously
installXApkDir(downloadedDir!, contextIfNewInstall,
needsBGWorkaround: true);
} else {
// ignore: use_build_context_synchronously
sayInstalled = await installXApkDir(
downloadedDir!, contextIfNewInstall,
shizukuPretendToBeGooglePlay:
apps[id]!.app.additionalSettings[
'shizukuPretendToBeGooglePlay'] ==
true);
}
}
if (willBeSilent && context == null) {
if (!settingsProvider.useShizuku) {
notificationsProvider?.notify(SilentUpdateAttemptNotification(
[apps[id]!.app],
id: id.hashCode));
} else {
notificationsProvider?.notify(SilentUpdateNotification(
[apps[id]!.app], sayInstalled,
id: id.hashCode));
}
}
if (sayInstalled) {
installedIds.add(id);
}
}
} finally {
apps[id]?.downloadProgress = null;
notifyListeners();
}
} catch (e) { } catch (e) {
errors.add(id, e, appName: apps[id]?.name); errors.add(id, e, appName: apps[id]?.name);
} }
return id; return {
'id': id,
'willBeSilent': willBeSilent,
'downloadedFile': downloadedFile,
'downloadedDir': downloadedDir
};
} }
List<Map<Object?, Object?>> downloadResults = [];
if (forceParallelDownloads || !settingsProvider.parallelDownloads) { if (forceParallelDownloads || !settingsProvider.parallelDownloads) {
for (var id in appsToInstall) { for (var id in appsToInstall) {
await updateFn(id); downloadResults.add(await downloadFn(id));
} }
} else { } else {
List<String> ids = await Future.wait( downloadResults = await Future.wait(
appsToInstall.map((id) => updateFn(id, skipInstalls: true))); appsToInstall.map((id) => downloadFn(id, skipInstalls: true)));
for (var id in ids) { }
if (!errors.appIdNames.containsKey(id)) { for (var res in downloadResults) {
await updateFn(id); if (!errors.appIdNames.containsKey(res['id'])) {
} await installFn(
res['id'] as String,
res['willBeSilent'] as bool,
res['downloadedFile'] as DownloadedApk?,
res['downloadedDir'] as DownloadedXApkDir?);
} }
} }
@@ -1144,17 +1166,6 @@ class AppsProvider with ChangeNotifier {
: false; : false;
} }
Future<void> updateInstallStatusInMemory(AppInMemory app) async {
apps[app.app.id]?.installedInfo = await getInstalledInfo(app.app.id);
apps[app.app.id]?.icon =
await apps[app.app.id]?.installedInfo?.applicationInfo?.getAppIcon();
apps[app.app.id]?.app.name = await (apps[app.app.id]
?.installedInfo
?.applicationInfo
?.getAppLabel()) ??
app.name;
}
Future<void> loadApps({String? singleId}) async { Future<void> loadApps({String? singleId}) async {
while (loadingApps) { while (loadingApps) {
await Future.delayed(const Duration(microseconds: 1)); await Future.delayed(const Duration(microseconds: 1));
@@ -1163,67 +1174,75 @@ class AppsProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
var sp = SourceProvider(); var sp = SourceProvider();
List<List<String>> errors = []; List<List<String>> errors = [];
List<App?> newApps = (await getAppsDir()) // Parse Apps from JSON var installedAppsData = await getAllInstalledInfo();
List<String> removedAppIds = [];
await Future.wait((await getAppsDir()) // Parse Apps from JSON
.listSync() .listSync()
.where((item) => item.path.toLowerCase().endsWith('.json')) .map((item) async {
.where((item) => App? app;
singleId == null || if (item.path.toLowerCase().endsWith('.json') &&
item.path.split('/').last.toLowerCase() == (singleId == null ||
'${singleId.toLowerCase()}.json') item.path.split('/').last.toLowerCase() ==
.map((e) { '${singleId.toLowerCase()}.json')) {
try { try {
return App.fromJson(jsonDecode(File(e.path).readAsStringSync())); app = App.fromJson(jsonDecode(File(item.path).readAsStringSync()));
} catch (err) { } catch (err) {
if (err is FormatException) { if (err is FormatException) {
logs.add('Corrupt JSON when loading App (will be ignored): $e'); logs.add('Corrupt JSON when loading App (will be ignored): $e');
e.renameSync('${e.path}.corrupt'); item.renameSync('${item.path}.corrupt');
} else { } else {
rethrow; rethrow;
}
} }
} }
}).toList();
for (var app in newApps) {
// Put Apps into memory to list them (fast)
if (app != null) { if (app != null) {
// Save the app to the in-memory list without grabbing any OS info first
apps.update(
app.id,
(value) => AppInMemory(
app!, value.downloadProgress, value.installedInfo, value.icon),
ifAbsent: () => AppInMemory(app!, null, null, null));
notifyListeners();
try { try {
// Try getting the app's source to ensure no invalid apps get loaded
sp.getSource(app.url, overrideSource: app.overrideSource); sp.getSource(app.url, overrideSource: app.overrideSource);
// If the app is installed, grab its OS data and reconcile install statuses
PackageInfo? installedInfo;
try {
installedInfo =
installedAppsData.firstWhere((i) => i.packageName == app!.id);
} catch (e) {
// If the app isn't installed the above throws an error
}
// Reconcile differences between the installed and recorded install info
var moddedApp =
getCorrectedInstallStatusAppIfPossible(app, installedInfo);
if (moddedApp != null) {
app = moddedApp;
// Note the app ID if it was uninstalled externally
if (moddedApp.installedVersion == null) {
removedAppIds.add(moddedApp.id);
}
}
// Update the app in memory with install info and corrections
apps.update( apps.update(
app.id, app.id,
(value) => AppInMemory( (value) => AppInMemory(
app, value.downloadProgress, value.installedInfo, value.icon), app!, value.downloadProgress, installedInfo, value.icon),
ifAbsent: () => AppInMemory(app, null, null, null)); ifAbsent: () => AppInMemory(app!, null, installedInfo, null));
notifyListeners();
} catch (e) { } catch (e) {
errors.add([app.id, app.finalName, e.toString()]); errors.add([app!.id, app.finalName, e.toString()]);
} }
} }
} }));
notifyListeners();
if (errors.isNotEmpty) { if (errors.isNotEmpty) {
removeApps(errors.map((e) => e[0]).toList()); removeApps(errors.map((e) => e[0]).toList());
NotificationsProvider().notify( NotificationsProvider().notify(
AppsRemovedNotification(errors.map((e) => [e[1], e[2]]).toList())); AppsRemovedNotification(errors.map((e) => [e[1], e[2]]).toList()));
} }
// Get install status and other OS info for each App (slow) // Delete externally uninstalled Apps if needed
await Future.wait(apps.values.map((app) { if (removedAppIds.isNotEmpty) {
return updateInstallStatusInMemory(app);
}));
notifyListeners();
// Reconcile version differences
List<App> modifiedApps = [];
for (var app in apps.values) {
var moddedApp =
getCorrectedInstallStatusAppIfPossible(app.app, app.installedInfo);
if (moddedApp != null) {
modifiedApps.add(moddedApp);
}
}
if (modifiedApps.isNotEmpty) {
await saveApps(modifiedApps, attemptToCorrectInstallStatus: false);
var removedAppIds = modifiedApps
.where((a) => a.installedVersion == null)
.map((e) => e.id)
.toList();
// After reconciliation, delete externally uninstalled Apps if needed
if (removedAppIds.isNotEmpty) { if (removedAppIds.isNotEmpty) {
if (settingsProvider.removeOnExternalUninstall) { if (settingsProvider.removeOnExternalUninstall) {
await removeApps(removedAppIds); await removeApps(removedAppIds);
@@ -1234,11 +1253,27 @@ class AppsProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
Future<void> updateAppIcon(String? appId) async {
if (apps[appId]?.icon == null) {
var icon =
(await apps[appId]?.installedInfo?.applicationInfo?.getAppIcon());
if (icon != null) {
apps.update(
apps[appId]!.app.id,
(value) => AppInMemory(apps[appId]!.app, value.downloadProgress,
value.installedInfo, icon),
ifAbsent: () => AppInMemory(
apps[appId]!.app, null, apps[appId]?.installedInfo, icon));
notifyListeners();
}
}
}
Future<void> saveApps(List<App> apps, Future<void> saveApps(List<App> apps,
{bool attemptToCorrectInstallStatus = true, {bool attemptToCorrectInstallStatus = true,
bool onlyIfExists = true}) async { bool onlyIfExists = true}) async {
attemptToCorrectInstallStatus = attemptToCorrectInstallStatus; attemptToCorrectInstallStatus = attemptToCorrectInstallStatus;
for (var a in apps) { await Future.wait(apps.map((a) async {
var app = a.deepCopy(); var app = a.deepCopy();
PackageInfo? info = await getInstalledInfo(app.id); PackageInfo? info = await getInstalledInfo(app.id);
var icon = await info?.applicationInfo?.getAppIcon(); var icon = await info?.applicationInfo?.getAppIcon();
@@ -1260,14 +1295,14 @@ class AppsProvider with ChangeNotifier {
rethrow; rethrow;
} }
} }
} }));
notifyListeners(); notifyListeners();
export(isAuto: true); export(isAuto: true);
} }
Future<void> removeApps(List<String> appIds) async { Future<void> removeApps(List<String> appIds) async {
var apkFiles = APKDir.listSync(); var apkFiles = APKDir.listSync();
for (var appId in appIds) { await Future.wait(appIds.map((appId) async {
File file = File('${(await getAppsDir()).path}/$appId.json'); File file = File('${(await getAppsDir()).path}/$appId.json');
if (file.existsSync()) { if (file.existsSync()) {
file.deleteSync(recursive: true); file.deleteSync(recursive: true);
@@ -1281,7 +1316,7 @@ class AppsProvider with ChangeNotifier {
if (apps.containsKey(appId)) { if (apps.containsKey(appId)) {
apps.remove(appId); apps.remove(appId);
} }
} }));
if (appIds.isNotEmpty) { if (appIds.isNotEmpty) {
notifyListeners(); notifyListeners();
export(isAuto: true); export(isAuto: true);
@@ -1516,7 +1551,7 @@ class AppsProvider with ChangeNotifier {
return returnPath; return returnPath;
} }
Future<MapEntry<int, bool>> import(String appsJSON) async { Future<MapEntry<List<App>, bool>> import(String appsJSON) async {
var decodedJSON = jsonDecode(appsJSON); var decodedJSON = jsonDecode(appsJSON);
var newFormat = decodedJSON is! List; var newFormat = decodedJSON is! List;
List<App> importedApps = List<App> importedApps =
@@ -1540,6 +1575,8 @@ class AppsProvider with ChangeNotifier {
settingsMap.forEach((key, value) { settingsMap.forEach((key, value) {
if (value is int) { if (value is int) {
settingsProvider.prefs?.setInt(key, value); settingsProvider.prefs?.setInt(key, value);
} else if (value is double) {
settingsProvider.prefs?.setDouble(key, value);
} else if (value is bool) { } else if (value is bool) {
settingsProvider.prefs?.setBool(key, value); settingsProvider.prefs?.setBool(key, value);
} else if (value is List) { } else if (value is List) {
@@ -1550,8 +1587,8 @@ class AppsProvider with ChangeNotifier {
} }
}); });
} }
return MapEntry<int, bool>( return MapEntry<List<App>, bool>(
importedApps.length, newFormat && decodedJSON['settings'] != null); importedApps, newFormat && decodedJSON['settings'] != null);
} }
@override @override

View File

@@ -354,11 +354,13 @@ preStandardizeUrl(String url) {
url.toLowerCase().indexOf('https://') != 0) { url.toLowerCase().indexOf('https://') != 0) {
url = 'https://$url'; url = 'https://$url';
} }
var trailingSlash = Uri.tryParse(url)?.path.endsWith('/') ?? false;
url = url url = url
.split('/') .split('/')
.where((e) => e.isNotEmpty) .where((e) => e.isNotEmpty)
.join('/') .join('/')
.replaceFirst(':/', '://'); .replaceFirst(':/', '://') +
(trailingSlash ? '/' : '');
return url; return url;
} }
@@ -523,8 +525,7 @@ abstract class AppSource {
[GeneratedFormTextField('appName', label: tr('appName'), required: false)], [GeneratedFormTextField('appName', label: tr('appName'), required: false)],
[ [
GeneratedFormSwitch('shizukuPretendToBeGooglePlay', GeneratedFormSwitch('shizukuPretendToBeGooglePlay',
label: tr('shizukuPretendToBeGooglePlay'), label: tr('shizukuPretendToBeGooglePlay'), defaultValue: false)
defaultValue: false)
], ],
[ [
GeneratedFormSwitch('exemptFromBackgroundUpdates', GeneratedFormSwitch('exemptFromBackgroundUpdates',

View File

@@ -47,18 +47,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: app_links name: app_links
sha256: "1c2b9e9c56d80d17610bcbd111b37187875c5d0ded8654caa1bda14ea753d001" sha256: "96e677810b83707ff5e10fac11e4839daa0ea4e0123c35864c092699165eb3db"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.1" version: "6.1.1"
archive: archive:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265 sha256: "6bd38d335f0954f5fad9c79e614604fbf03a0e5b975923dd001b6ea965ef5b4b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.5.1" version: "3.6.0"
args: args:
dependency: transitive dependency: transitive
description: description:
@@ -215,10 +215,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: easy_localization name: easy_localization
sha256: "432698c31a488dd64c56d4759f20d04844baba5e9e4f2cb1abb9676257918b17" sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.6" version: "3.0.7"
easy_logger: easy_logger:
dependency: transitive dependency: transitive
description: description:
@@ -279,18 +279,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flex_color_picker name: flex_color_picker
sha256: "5c846437069fb7afdd7ade6bf37e628a71d2ab0787095ddcb1253bf9345d5f3a" sha256: "31b27677d8d8400e4cff5edb3f189f606dd964d608779b6ae1b7ddad37ea48c6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.4.1" version: "3.5.0"
flex_seed_scheme: flex_seed_scheme:
dependency: transitive dependency: transitive
description: description:
name: flex_seed_scheme name: flex_seed_scheme
sha256: "4cee2f1d07259f77e8b36f4ec5f35499d19e74e17c7dce5b819554914082bc01" sha256: fb66cdb8ca89084e79efcad2bc2d9deb144666875116f08cdd8d9f8238c8b3ab
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "2.0.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@@ -324,18 +324,18 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "4.0.0"
flutter_local_notifications: flutter_local_notifications:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_local_notifications name: flutter_local_notifications
sha256: "84a3af6c7fb43c85c3528b434dacc7a7ed4551d1209d93773bf6045cec9ace68" sha256: "40e6fbd2da7dcc7ed78432c5cdab1559674b4af035fddbfb2f9a8f9c2112fcef"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "17.1.1" version: "17.1.2"
flutter_local_notifications_linux: flutter_local_notifications_linux:
dependency: transitive dependency: transitive
description: description:
@@ -443,18 +443,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image name: image
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.7" version: "4.2.0"
intl: intl:
dependency: transitive dependency: transitive
description: description:
name: intl name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.19.0"
json_annotation: json_annotation:
dependency: transitive dependency: transitive
description: description:
@@ -467,36 +467,36 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.0" version: "10.0.4"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "3.0.3"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "3.0.1"
lints: lints:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "4.0.0"
markdown: markdown:
dependency: transitive dependency: "direct main"
description: description:
name: markdown name: markdown
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
@@ -523,10 +523,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@@ -579,10 +579,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_foundation name: path_provider_foundation
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.4.0"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@@ -723,10 +723,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_foundation name: shared_preferences_foundation
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.5" version: "2.4.0"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
@@ -857,10 +857,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.1" version: "0.7.0"
timezone: timezone:
dependency: transitive dependency: transitive
description: description:
@@ -889,18 +889,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775" sha256: "17cd5e205ea615e2c6ea7a77323a11712dffa0720a8a90540db57a01347f9ad9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.1" version: "6.3.2"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.5" version: "6.3.0"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@@ -913,10 +913,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_macos name: url_launcher_macos
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.2.0"
url_launcher_platform_interface: url_launcher_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -961,10 +961,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "13.0.0" version: "14.2.1"
web: web:
dependency: transitive dependency: transitive
description: description:
@@ -977,18 +977,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: webview_flutter name: webview_flutter
sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932" sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.7.0" version: "4.8.0"
webview_flutter_android: webview_flutter_android:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: dad3313c9ead95517bb1cae5e1c9d20ba83729d5a59e5e83c0a2d66203f27f91 sha256: "2282ba2320af34b2bd5320156c664d73f3f022341ed78847bc87723bf88c142f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.16.1" version: "3.16.2"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -1001,18 +1001,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7 sha256: "7affdf9d680c015b11587181171d3cad8093e449db1f7d9f0f08f4f33d24f9a0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.13.0" version: "3.13.1"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.0" version: "5.5.1"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:
@@ -1046,5 +1046,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.3.3 <4.0.0" dart: ">=3.4.0 <4.0.0"
flutter: ">=3.19.0" flutter: ">=3.22.0"

View File

@@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.1.7+2264 version: 1.1.10+2267
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
@@ -79,6 +79,7 @@ dependencies:
url: https://github.com/re7gog/shizuku_apk_installer url: https://github.com/re7gog/shizuku_apk_installer
ref: master ref: master
markdown: any
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
@@ -89,7 +90,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your # activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint # package. See that file for information about deactivating specific lint
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^3.0.0 flutter_lints: ^4.0.0
flutter_launcher_icons: flutter_launcher_icons:
android: "ic_launcher" android: "ic_launcher"