diff --git a/.flutter b/.flutter index d693b4b..9f455d2 160000 --- a/.flutter +++ b/.flutter @@ -1 +1 @@ -Subproject commit d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 +Subproject commit 9f455d2486bcb28cad87b062475f42edc959f636 diff --git a/assets/translations/ar.json b/assets/translations/ar.json index 1867e4c..36bc39c 100644 --- a/assets/translations/ar.json +++ b/assets/translations/ar.json @@ -337,6 +337,8 @@ "fgServiceNotice": "هذا الإشعار مطلوب للتحقق من التحديث في الخلفية (يمكن إخفاؤه في إعدادات نظام التشغيل)", "excludeSecrets": "استبعاد الأسرار", "GHReqPrefix": "مثيل \"sky22333/hubproxy\" لطلبات GitHub", + "includeZips": "تضمين ملفات ZIP", + "zippedApkFilterRegEx": "تصفية ملفات APK داخل ZIP", "removeAppQuestion": { "one": "إزالة التطبيق؟", "other": "إزالة التطبيقات؟" diff --git a/assets/translations/bs.json b/assets/translations/bs.json index 8641173..b10b7eb 100644 --- a/assets/translations/bs.json +++ b/assets/translations/bs.json @@ -337,6 +337,8 @@ "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "excludeSecrets": "Exclude secrets", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", + "includeZips": "Include ZIP files", + "zippedApkFilterRegEx": "Filter APKs inside ZIP", "removeAppQuestion": { "one": "Želite li ukloniti aplikaciju?", "other": "Želite li ukloniti aplikacije?" diff --git a/assets/translations/ca.json b/assets/translations/ca.json index 4b7d5d1..de000af 100644 --- a/assets/translations/ca.json +++ b/assets/translations/ca.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Aquesta notificació és necessària per comprovar les actualitzacions en segon pla (la pots ocultar als paràmetres del Sistema Operatiu)", "excludeSecrets": "Exclude secrets", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", + "includeZips": "Include ZIP files", + "zippedApkFilterRegEx": "Filter APKs inside ZIP", "removeAppQuestion": { "one": "¿Suprimeixo l'aplicació?", "other": "¿Suprimeixo les aplicacions?" diff --git a/assets/translations/cs.json b/assets/translations/cs.json index 5f81c07..bf19805 100644 --- a/assets/translations/cs.json +++ b/assets/translations/cs.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Toto oznámení je nutné pro kontrolu aktualizací na pozadí (lze jej skrýt v nastavení systému)", "excludeSecrets": "Vyloučit tajemství", "GHReqPrefix": "instance 'sky22333/hubproxy' pro požadavky GitHubu", + "includeZips": "Zahrnout soubory ZIP", + "zippedApkFilterRegEx": "Filtrování souborů APK uvnitř ZIP", "removeAppQuestion": { "one": "Odstranit aplikaci?", "other": "Odstranit aplikace?" diff --git a/assets/translations/da.json b/assets/translations/da.json index 059a7e4..e34389f 100644 --- a/assets/translations/da.json +++ b/assets/translations/da.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Denne meddelelse er nødvendig for baggrundsopdateringskontrol (den kan skjules i OS-indstillingerne).", "excludeSecrets": "Udeluk hemmeligheder", "GHReqPrefix": "'sky22333/hubproxy'-instans til GitHub-anmodninger", + "includeZips": "Inkluder ZIP-filer", + "zippedApkFilterRegEx": "Filtrer APK'er inde i ZIP", "removeAppQuestion": { "one": "Fjern app?", "other": "Fjern apps?" diff --git a/assets/translations/de.json b/assets/translations/de.json index 0a85f31..77f452b 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Diese Benachrichtigung ist für die Prüfung von Updates im Hintergrund erforderlich (sie kann in den Betriebssystemeinstellungen ausgeblendet werden)", "excludeSecrets": "Geheimnisse ausschließen", "GHReqPrefix": "sky22333/hubproxy'-Instanz für GitHub-Anfragen", + "includeZips": "ZIP-Dateien einbeziehen", + "zippedApkFilterRegEx": "APKs in ZIP filtern", "removeAppQuestion": { "one": "App entfernen?", "other": "Apps entfernen?" diff --git a/assets/translations/en-EO.json b/assets/translations/en-EO.json index 060db6e..db5dc75 100644 --- a/assets/translations/en-EO.json +++ b/assets/translations/en-EO.json @@ -337,6 +337,8 @@ "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "excludeSecrets": "Exclude secrets", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", + "includeZips": "Include ZIP files", + "zippedApkFilterRegEx": "Filter APKs inside ZIP", "removeAppQuestion": { "one": "Forigi la aplikaĵon?", "other": "Forigi la aplikaĵojn?" diff --git a/assets/translations/en.json b/assets/translations/en.json index 0fd891a..1bbcb76 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -337,6 +337,8 @@ "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "excludeSecrets": "Exclude secrets", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", + "includeZips": "Include ZIP files", + "zippedApkFilterRegEx": "Filter APKs inside ZIP", "removeAppQuestion": { "one": "Remove app?", "other": "Remove apps?" diff --git a/assets/translations/es.json b/assets/translations/es.json index bf6da72..5a977b8 100644 --- a/assets/translations/es.json +++ b/assets/translations/es.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Esta notificación es necesaria para la comprobación de actualizaciones en segundo plano (puede ocultarse en la configuración del sistema operativo).", "excludeSecrets": "Excluir secretos", "GHReqPrefix": "Instancia \"sky22333/hubproxy\" para las solicitudes de GitHub", + "includeZips": "Incluir archivos ZIP", + "zippedApkFilterRegEx": "Filtrar APKs dentro de ZIP", "removeAppQuestion": { "one": "¿Eliminar aplicación?", "other": "¿Eliminar aplicaciones?" diff --git a/assets/translations/fa.json b/assets/translations/fa.json index 902c409..7599062 100644 --- a/assets/translations/fa.json +++ b/assets/translations/fa.json @@ -337,6 +337,8 @@ "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "excludeSecrets": "Exclude secrets", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", + "includeZips": "Include ZIP files", + "zippedApkFilterRegEx": "Filter APKs inside ZIP", "removeAppQuestion": { "one": "برنامه حذف شود؟", "other": "برنامه ها حذف شوند؟" diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 6b8fc5b..9a3f25e 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Cette notification est nécessaire pour la vérification des mises à jour en arrière-plan (elle peut être masquée dans les paramètres du système d'exploitation).", "excludeSecrets": "Exclure les secrets", "GHReqPrefix": "instance 'sky22333/hubproxy' pour les requêtes GitHub", + "includeZips": "Inclure les fichiers ZIP", + "zippedApkFilterRegEx": "Filtrer les APK à l'intérieur du ZIP", "removeAppQuestion": { "one": "Supprimer l'application ?", "other": "Supprimer les applications ?" diff --git a/assets/translations/hu.json b/assets/translations/hu.json index 81de3f0..6ec8d14 100644 --- a/assets/translations/hu.json +++ b/assets/translations/hu.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Ez az értesítés a háttérben történő frissítésellenőrzéshez szükséges (a rendszer beállításaiban elrejthető).", "excludeSecrets": "Érzékeny adatok (például: személyes hozzáférési tokenek) kihagyása", "GHReqPrefix": "„sky22333/hubproxy” példány a GitHub lekérdezéséhez", + "includeZips": "ZIP fájlok belefoglalása", + "zippedApkFilterRegEx": "APK-k szűrése a ZIP-en belül", "removeAppQuestion": { "one": "Eltávolítja az alkalmazást?", "other": "Eltávolítja az alkalmazásokat?" diff --git a/assets/translations/id.json b/assets/translations/id.json index ab90b0e..5298614 100644 --- a/assets/translations/id.json +++ b/assets/translations/id.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Pemberitahuan ini diperlukan untuk pemeriksaan pembaruan latar belakang (dapat disembunyikan dalam pengaturan OS)", "excludeSecrets": "Mengecualikan rahasia", "GHReqPrefix": "Instance 'sky22333/hubproxy' untuk permintaan GitHub", + "includeZips": "Menyertakan file ZIP", + "zippedApkFilterRegEx": "Saring APK di dalam ZIP", "removeAppQuestion": { "one": "Hapus aplikasi?", "other": "Hapus aplikasi?" diff --git a/assets/translations/it.json b/assets/translations/it.json index 50c051e..d0d85a7 100644 --- a/assets/translations/it.json +++ b/assets/translations/it.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Questa notifica è necessaria per il controllo degli aggiornamenti in background (può essere nascosta nelle impostazioni del sistema operativo).", "excludeSecrets": "Escludere i segreti", "GHReqPrefix": "istanza 'sky22333/hubproxy' per le richieste a GitHub", + "includeZips": "Includere file ZIP", + "zippedApkFilterRegEx": "Filtrare gli APK all'interno dello ZIP", "removeAppQuestion": { "one": "Rimuovere l'app?", "other": "Rimuovere le app?" diff --git a/assets/translations/ja.json b/assets/translations/ja.json index 64a4997..a00a26e 100644 --- a/assets/translations/ja.json +++ b/assets/translations/ja.json @@ -337,6 +337,8 @@ "fgServiceNotice": "この通知は、バックグラウンドでアップデートを確認するために必要です(OSの設定で非表示にできます)。", "excludeSecrets": "シークレットを除く", "GHReqPrefix": "GitHub リクエスト用の 'sky22333/hubproxy' インスタンス", + "includeZips": "ZIPファイルを含む", + "zippedApkFilterRegEx": "ZIP内のAPKをフィルタリングする", "removeAppQuestion": { "one": "アプリを削除しますか?", "other": "アプリを削除しますか?" diff --git a/assets/translations/ko.json b/assets/translations/ko.json index 6ec4cda..e4f2a62 100644 --- a/assets/translations/ko.json +++ b/assets/translations/ko.json @@ -337,6 +337,8 @@ "fgServiceNotice": "이 알림은 백그라운드 업데이트 확인에 필요합니다(OS 설정에서 숨길 수 있음).", "excludeSecrets": "비밀 제외", "GHReqPrefix": "GitHub 요청을 위한 'sky22333/hubproxy' 인스턴스", + "includeZips": "ZIP 파일 포함", + "zippedApkFilterRegEx": "ZIP 내 APK 필터링", "removeAppQuestion": { "one": "앱을 제거하시겠습니까?", "other": "앱을 제거하시겠습니까?" diff --git a/assets/translations/ml.json b/assets/translations/ml.json index 93dae6f..0d73063 100644 --- a/assets/translations/ml.json +++ b/assets/translations/ml.json @@ -337,6 +337,8 @@ "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "excludeSecrets": "Exclude secrets", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", + "includeZips": "Include ZIP files", + "zippedApkFilterRegEx": "Filter APKs inside ZIP", "removeAppQuestion": { "one": "ആപ്പ് നീക്കം ചെയ്യണോ?", "other": "ആപ്പുകൾ നീക്കം ചെയ്യണോ?" diff --git a/assets/translations/nl.json b/assets/translations/nl.json index cfe1d7d..b8308f6 100644 --- a/assets/translations/nl.json +++ b/assets/translations/nl.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Deze melding is nodig voor het controleren van updates op de achtergrond (kan worden verborgen in de OS-instellingen)", "excludeSecrets": "Geheimen uitsluiten", "GHReqPrefix": "'sky22333/hubproxy' instantie voor GitHub verzoeken", + "includeZips": "ZIP-bestanden opnemen", + "zippedApkFilterRegEx": "APK's filteren in ZIP", "removeAppQuestion": { "one": "App verwijderen?", "other": "Apps verwijderen?" diff --git a/assets/translations/pl.json b/assets/translations/pl.json index 81e609f..213ffe4 100644 --- a/assets/translations/pl.json +++ b/assets/translations/pl.json @@ -337,6 +337,8 @@ "fgServiceNotice": "To powiadomienie jest wymagane do sprawdzania aktualizacji w tle (można je ukryć w ustawieniach systemu operacyjnego).", "excludeSecrets": "Wyklucz sekrety", "GHReqPrefix": "Instancja \"sky22333/hubproxy\" dla żądań GitHub", + "includeZips": "Dołączanie plików ZIP", + "zippedApkFilterRegEx": "Filtrowanie plików APK wewnątrz ZIP", "removeAppQuestion": { "one": "Usunąć aplikację?", "few": "Usunąć aplikacje?", diff --git a/assets/translations/pt-BR.json b/assets/translations/pt-BR.json index cb385b5..73abdd3 100644 --- a/assets/translations/pt-BR.json +++ b/assets/translations/pt-BR.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Essa notificação é necessária para a verificação de atualizações em segundo plano (ela pode ser ocultada nas configurações do sistema operacional)", "excludeSecrets": "Excluir segredos", "GHReqPrefix": "Instância \"sky22333/hubproxy\" para solicitações do GitHub", + "includeZips": "Incluir arquivos ZIP", + "zippedApkFilterRegEx": "Filtrar APKs dentro do ZIP", "removeAppQuestion": { "one": "Remover app?", "other": "Remover apps?" diff --git a/assets/translations/pt.json b/assets/translations/pt.json index 789bcab..68b3c2b 100644 --- a/assets/translations/pt.json +++ b/assets/translations/pt.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Esta notificação é necessária para a verificação de actualizações em segundo plano (pode ser ocultada nas definições do SO)", "excludeSecrets": "Excluir segredos", "GHReqPrefix": "Instância 'sky22333/hubproxy' para pedidos de GitHub", + "includeZips": "Incluir ficheiros ZIP", + "zippedApkFilterRegEx": "Filtrar APKs dentro do ZIP", "removeAppQuestion": { "one": "Remover aplicação?", "other": "Remover aplicações?" diff --git a/assets/translations/ru.json b/assets/translations/ru.json index d14f5df..8d3d579 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Это уведомление необходимо для фоновой проверки обновлений (оно может быть скрыто в настройках ОС)", "excludeSecrets": "Исключить секреты", "GHReqPrefix": "Экземпляр 'sky22333/hubproxy' для запросов на GitHub", + "includeZips": "Включить ZIP-файлы", + "zippedApkFilterRegEx": "Фильтр APK внутри ZIP", "removeAppQuestion": { "one": "Удалить приложение?", "other": "Удалить приложения?" diff --git a/assets/translations/sv.json b/assets/translations/sv.json index 83ef1f0..01b02ea 100644 --- a/assets/translations/sv.json +++ b/assets/translations/sv.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Detta meddelande krävs för bakgrundsuppdateringskontroll (det kan döljas i OS-inställningarna)", "excludeSecrets": "Utesluta hemligheter", "GHReqPrefix": "Instansen \"sky22333/hubproxy\" för GitHub-förfrågningar", + "includeZips": "Inkludera ZIP-filer", + "zippedApkFilterRegEx": "Filtrera APK:er inuti ZIP", "removeAppQuestion": { "one": "Ta Bort App?", "other": "Ta Bort Appar?" diff --git a/assets/translations/tr.json b/assets/translations/tr.json index 87450de..08e9f4c 100644 --- a/assets/translations/tr.json +++ b/assets/translations/tr.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Bu bildirim arka planda güncelleme kontrolü için gereklidir (işletim sistemi ayarlarından gizlenebilir)", "excludeSecrets": "Sırları hariç tut", "GHReqPrefix": "GitHub istekleri için 'sky22333/hubproxy' örneği", + "includeZips": "ZIP dosyalarını dahil edin", + "zippedApkFilterRegEx": "ZIP içindeki APK'ları filtreleme", "removeAppQuestion": { "one": "Uygulamayı Kaldır?", "other": "Uygulamaları Kaldır?" diff --git a/assets/translations/uk.json b/assets/translations/uk.json index f634688..a3f720a 100644 --- a/assets/translations/uk.json +++ b/assets/translations/uk.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Це сповіщення необхідне для фонової перевірки оновлень (його можна приховати в налаштуваннях ОС)", "excludeSecrets": "Виключити секрети", "GHReqPrefix": "екземпляр 'sky22333/hubproxy' для запитів на GitHub", + "includeZips": "Додайте ZIP-файли", + "zippedApkFilterRegEx": "Фільтруйте APK-файли всередині ZIP", "removeAppQuestion": { "one": "Видалити застосунок?", "other": "Видалити застосунки?" diff --git a/assets/translations/vi.json b/assets/translations/vi.json index 2933d6a..40966cd 100644 --- a/assets/translations/vi.json +++ b/assets/translations/vi.json @@ -337,6 +337,8 @@ "fgServiceNotice": "Thông báo này là bắt buộc để kiểm tra cập nhật nền (có thể ẩn trong cài đặt hệ điều hành).", "excludeSecrets": "Loại trừ thông tin bí mật", "GHReqPrefix": "Thực thể 'sky22333/hubproxy' cho các yêu cầu GitHub", + "includeZips": "Ba gồm các tệp ZIP", + "zippedApkFilterRegEx": "Lọc các tệp APK bên trong tệp ZIP", "removeAppQuestion": { "one": "Gỡ ứng dụng?", "other": "Gỡ ứng dụng?" diff --git a/assets/translations/zh-Hant-TW.json b/assets/translations/zh-Hant-TW.json index da52339..27168a1 100644 --- a/assets/translations/zh-Hant-TW.json +++ b/assets/translations/zh-Hant-TW.json @@ -337,6 +337,8 @@ "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "excludeSecrets": "Exclude secrets", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", + "includeZips": "Include ZIP files", + "zippedApkFilterRegEx": "Filter APKs inside ZIP", "removeAppQuestion": { "one": "移除應用程式?", "other": "移除應用程式?" diff --git a/assets/translations/zh.json b/assets/translations/zh.json index e94b4de..9c9f14e 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -337,6 +337,8 @@ "fgServiceNotice": "后台检查更新时需要此通知(可在操作系统设置中隐藏)", "excludeSecrets": "排除机密", "GHReqPrefix": "用于 GitHub 请求的 \"sky22333/hubproxy \"实例", + "includeZips": "包含 ZIP 文件", + "zippedApkFilterRegEx": "过滤 ZIP 内的 APK", "removeAppQuestion": { "one": "是否删除应用?", "other": "是否删除应用?" diff --git a/lib/app_sources/github.dart b/lib/app_sources/github.dart index f9c53d3..fe4a2d9 100644 --- a/lib/app_sources/github.dart +++ b/lib/app_sources/github.dart @@ -18,6 +18,7 @@ class GitHub extends AppSource { appIdInferIsOptional = true; showReleaseDateAsVersionToggle = true; this.hostChanged = hostChanged; + allowIncludeZips = true; sourceConfigSettingFormItems = [ GeneratedFormTextField( @@ -370,6 +371,7 @@ class GitHub extends AppSource { additionalSettings['useLatestAssetDateAsReleaseDate'] == true; String sortMethod = additionalSettings['sortMethodChoice'] ?? 'smartname-datefallback'; + bool includeZips = additionalSettings['includeZips'] == true; dynamic latestRelease; if (verifyLatestTag) { var temp = requestUrl.split('?'); @@ -402,7 +404,8 @@ class GitHub extends AppSource { findReleaseAssetUrls(dynamic release) => (release['assets'] as List?)?.map((e) { - var url = !e['name'].toString().toLowerCase().endsWith('.apk') + var ext = e['name'].toString().toLowerCase().split('.').last; + var url = !(ext == 'apk' || (includeZips && ext == 'zip')) ? (e['browser_download_url'] ?? e['url']) : (e['url'] ?? e['browser_download_url']); url = undoGHProxyMod(url, sourceConfigSettingValues); @@ -542,14 +545,13 @@ class GitHub extends AppSource { List> allAssetUrls = allAssetsWithUrls .map((e) => e['final_url'] as MapEntry) .toList(); - var apkAssetsWithUrls = allAssetsWithUrls - .where( - (element) => (element['final_url'] as MapEntry) - .key - .toLowerCase() - .endsWith('.apk'), - ) - .toList(); + var apkAssetsWithUrls = allAssetsWithUrls.where((element) { + var ext = (element['final_url'] as MapEntry).key + .toLowerCase() + .split('.') + .last; + return ext == 'apk' || (includeZips && ext == 'zip'); + }).toList(); var filteredApkUrls = filterApks( apkAssetsWithUrls diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index 4c222c9..589eed7 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -203,11 +203,11 @@ class AddAppPageState extends State { notificationsProvider: notificationsProvider, ); DownloadedApk? downloadedFile; - DownloadedXApkDir? downloadedDir; + DownloadedDir? downloadedDir; if (downloadedArtifact is DownloadedApk) { downloadedFile = downloadedArtifact; } else { - downloadedDir = downloadedArtifact as DownloadedXApkDir; + downloadedDir = downloadedArtifact as DownloadedDir; } app.id = downloadedFile?.appId ?? downloadedDir!.appId; } diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index 71e9a73..64196cb 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -1010,6 +1010,7 @@ class AppsPageState extends State { selectedApps.where((element) => element.pinned).isEmpty ? tr('pinToTop') : tr('unpinFromTop'), + textAlign: TextAlign.center, ), ), const Divider(), @@ -1026,7 +1027,10 @@ class AppsPageState extends State { ); Navigator.of(context).pop(); }, - child: Text(tr('shareSelectedAppURLs')), + child: Text( + tr('shareSelectedAppURLs'), + textAlign: TextAlign.center, + ), ), const Divider(), TextButton( @@ -1043,7 +1047,10 @@ class AppsPageState extends State { subject: 'Obtainium - ${tr('appsString')}', ); }, - child: Text(tr('shareAppConfigLinks')), + child: Text( + tr('shareAppConfigLinks'), + textAlign: TextAlign.center, + ), ), const Divider(), TextButton( @@ -1069,7 +1076,10 @@ class AppsPageState extends State { fileNameOverrides: ['$fn.json'], ); }, - child: Text('${tr('share')} - ${tr('obtainiumExport')}'), + child: Text( + '${tr('share')} - ${tr('obtainiumExport')}', + textAlign: TextAlign.center, + ), ), const Divider(), TextButton( @@ -1093,6 +1103,7 @@ class AppsPageState extends State { 'downloadX', args: [lowerCaseIfEnglish(tr('releaseAsset'))], ), + textAlign: TextAlign.center, ), ), const Divider(), @@ -1100,7 +1111,10 @@ class AppsPageState extends State { onPressed: appsProvider.areDownloadsRunning() ? null : showMassMarkDialog, - child: Text(tr('markSelectedAppsUpdated')), + child: Text( + tr('markSelectedAppsUpdated'), + textAlign: TextAlign.center, + ), ), ], ), diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 4c0c96b..7770cce 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -62,11 +62,14 @@ class DownloadedApk { DownloadedApk(this.appId, this.file); } -class DownloadedXApkDir { +enum DownloadedDirType { XAPK, ZIP } + +class DownloadedDir { String appId; File file; Directory extracted; - DownloadedXApkDir(this.appId, this.file, this.extracted); + DownloadedDirType type; + DownloadedDir(this.appId, this.file, this.extracted, this.type); } List generateStandardVersionRegExStrings() { @@ -664,17 +667,18 @@ class AppsProvider with ChangeNotifier { } PackageInfo? newInfo; var isAPK = downloadedFile.path.toLowerCase().endsWith('.apk'); - Directory? xapkDir; + var isXAPK = downloadedFile.path.toLowerCase().endsWith('.xapk'); + Directory? apkDir; if (isAPK) { newInfo = await pm.getPackageArchiveInfo( archiveFilePath: downloadedFile.path, ); } else { - // Assume XAPK - String xapkDirPath = '${downloadedFile.path}-dir'; + // Assume XAPK or ZIP + String apkDirPath = '${downloadedFile.path}-dir'; await unzipFile(downloadedFile.path, '${downloadedFile.path}-dir'); - xapkDir = Directory(xapkDirPath); - var apks = xapkDir + apkDir = Directory(apkDirPath); + var apks = apkDir .listSync() .where((e) => e.path.toLowerCase().endsWith('.apk')) .toList(); @@ -691,6 +695,22 @@ class AppsProvider with ChangeNotifier { apks = [temp!, ...apks]; } + if (app.additionalSettings['zippedApkFilterRegEx']?.isNotEmpty == + true) { + var reg = RegExp(app.additionalSettings['zippedApkFilterRegEx']); + apks.removeWhere((apk) { + var shouldDelete = !reg.hasMatch(apk.uri.pathSegments.last); + if (shouldDelete) { + apk.delete(); + } + return shouldDelete; + }); + } + + if (apks.isEmpty) { + throw NoAPKError(); + } + for (var i = 0; i < apks.length; i++) { try { newInfo = await pm.getPackageArchiveInfo( @@ -728,7 +748,12 @@ class AppsProvider with ChangeNotifier { if (isAPK) { return DownloadedApk(app.id, downloadedFile); } else { - return DownloadedXApkDir(app.id, downloadedFile, xapkDir!); + return DownloadedDir( + app.id, + downloadedFile, + apkDir!, + isXAPK ? DownloadedDirType.XAPK : DownloadedDirType.ZIP, + ); } } finally { notificationsProvider?.cancel(notifId); @@ -826,15 +851,16 @@ class AppsProvider with ChangeNotifier { ); } - Future installXApkDir( - DownloadedXApkDir dir, + Future installApkDir( + DownloadedDir dir, BuildContext? firstTimeWithContext, { bool needsBGWorkaround = false, bool shizukuPretendToBeGooglePlay = false, }) async { - // We don't know which APKs in an XAPK are supported by the user's device + // We don't know which APKs in an XAPK or ZIP are supported by the user's device // So we try installing all of them and assume success if at least one installed // If 0 APKs installed, throw the first install error encountered + // Obviously this approach is naive and is undesirable in many cases, needs to be improved var somethingInstalled = false; try { MultiAppMultiError errors = MultiAppMultiError(); @@ -863,7 +889,7 @@ class AppsProvider with ChangeNotifier { } try { - await installApk( + var wasInstalled = await installApk( DownloadedApk(dir.appId, APKFiles[0]), firstTimeWithContext, needsBGWorkaround: needsBGWorkaround, @@ -872,10 +898,10 @@ class AppsProvider with ChangeNotifier { 1, ).map((a) => DownloadedApk(dir.appId, a)).toList(), ); - somethingInstalled = true; + somethingInstalled = somethingInstalled || wasInstalled; dir.file.delete(recursive: true); } catch (e) { - logs.add('Could not install APKs from XAPK: ${e.toString()}'); + logs.add('Could not install APKs from ${dir.type}: ${e.toString()}'); errors.add(dir.appId, e, appName: apps[dir.appId]?.name); } if (errors.idsByErrorString.isNotEmpty) { @@ -1148,7 +1174,7 @@ class AppsProvider with ChangeNotifier { String id, bool willBeSilent, DownloadedApk? downloadedFile, - DownloadedXApkDir? downloadedDir, + DownloadedDir? downloadedDir, ) async { apps[id]?.downloadProgress = -1; notifyListeners(); @@ -1183,14 +1209,14 @@ class AppsProvider with ChangeNotifier { } else { if (needBGWorkaround) { // ignore: use_build_context_synchronously - installXApkDir( + installApkDir( downloadedDir!, contextIfNewInstall, needsBGWorkaround: true, ); } else { // ignore: use_build_context_synchronously - sayInstalled = await installXApkDir( + sayInstalled = await installApkDir( downloadedDir!, contextIfNewInstall, shizukuPretendToBeGooglePlay: shizukuPretendToBeGooglePlay, @@ -1227,7 +1253,7 @@ class AppsProvider with ChangeNotifier { }) async { bool willBeSilent = false; DownloadedApk? downloadedFile; - DownloadedXApkDir? downloadedDir; + DownloadedDir? downloadedDir; try { var downloadedArtifact = // ignore: use_build_context_synchronously @@ -1240,7 +1266,7 @@ class AppsProvider with ChangeNotifier { if (downloadedArtifact is DownloadedApk) { downloadedFile = downloadedArtifact; } else { - downloadedDir = downloadedArtifact as DownloadedXApkDir; + downloadedDir = downloadedArtifact as DownloadedDir; } id = downloadedFile?.appId ?? downloadedDir!.appId; willBeSilent = await canInstallSilently(apps[id]!.app); @@ -1292,7 +1318,7 @@ class AppsProvider with ChangeNotifier { res['id'] as String, res['willBeSilent'] as bool, res['downloadedFile'] as DownloadedApk?, - res['downloadedDir'] as DownloadedXApkDir?, + res['downloadedDir'] as DownloadedDir?, ); } catch (e) { var id = res['id'] as String; @@ -1323,7 +1349,8 @@ class AppsProvider with ChangeNotifier { MapEntry? fileUrl; var refreshBeforeDownload = apps[id]!.app.additionalSettings['refreshBeforeDownload'] == true || - apps[id]!.app.apkUrls.first.value == 'placeholder'; + apps[id]!.app.apkUrls.isNotEmpty && + apps[id]!.app.apkUrls.first.value == 'placeholder'; if (refreshBeforeDownload) { await checkUpdate(apps[id]!.app.id); } diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index b7dc317..f4e1f28 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -636,6 +636,7 @@ abstract class AppSource { bool versionDetectionDisallowed = false; List excludeCommonSettingKeys = []; bool urlsAlwaysHaveExtension = false; + bool allowIncludeZips = false; AppSource() { name = runtimeType.toString(); @@ -834,7 +835,7 @@ abstract class AppSource { ], ]; - // Previous 2 variables combined into one at runtime for convenient usage + // Previous 2 variables combined into one at runtime for convenient usage + additional processing List> get combinedAppSpecificSettingFormItems { if (showReleaseDateAsVersionToggle == true) { if (additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly @@ -878,6 +879,32 @@ abstract class AppSource { ) .where((e) => e.isNotEmpty) .toList(); + + var moreConditionalItems = []; + if (allowIncludeZips) { + moreConditionalItems.addAll([ + [ + GeneratedFormSwitch( + 'includeZips', + label: tr('includeZips'), + defaultValue: false, + ), + ], + [ + GeneratedFormTextField( + 'zippedApkFilterRegEx', + label: tr('zippedApkFilterRegEx'), + required: false, + additionalValidators: [ + (value) { + return regExValidator(value); + }, + ], + ), + ], + ]); + } + if (versionDetectionDisallowed) { overrideAdditionalAppSpecificSourceAgnosticSettingSwitch( 'versionDetection', @@ -893,6 +920,7 @@ abstract class AppSource { return [ ...additionalSourceAppSpecificSettingFormItems, ...additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly, + ...moreConditionalItems, ]; } diff --git a/pubspec.lock b/pubspec.lock index 05247eb..218286b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -40,10 +40,10 @@ packages: dependency: "direct main" description: name: animations - sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb + sha256: a8031b276f0a7986ac907195f10ca7cd04ecf2a8a566bd6dbe03018a9b02b427 url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.1.0" app_links: dependency: "direct main" description: @@ -482,10 +482,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: b0694b7fb1689b0e6cc193b3f1fcac6423c4f93c74fb20b806c6b6f196db0c31 + sha256: c2fe1001710127dfa7da89977a08d591398370d099aacdaa6d44da7eb14b8476 url: "https://pub.dev" source: hosted - version: "2.0.30" + version: "2.0.31" flutter_test: dependency: transitive description: flutter @@ -676,10 +676,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "993381400e94d18469750e5b9dcb8206f15bc09f9da86b9e44a9b0092a0066db" + sha256: "3b4c1fc3aa55ddc9cd4aa6759984330d5c8e66aa7702a6223c61540dc6380c37" url: "https://pub.dev" source: hosted - version: "2.2.18" + version: "2.2.19" path_provider_foundation: dependency: transitive description: @@ -852,10 +852,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: bd14436108211b0d4ee5038689a56d4ae3620fd72fd6036e113bf1345bc74d9e + sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713" url: "https://pub.dev" source: hosted - version: "2.4.13" + version: "2.4.15" shared_preferences_foundation: dependency: transitive description: @@ -1059,10 +1059,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "199bc33e746088546a39cc5f36bac5a278c5e53b40cb3196f99e7345fdcfae6b" + sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9" url: "https://pub.dev" source: hosted - version: "6.3.22" + version: "6.3.24" url_launcher_ios: dependency: transitive description: @@ -1155,10 +1155,10 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: "3c4eb4fcc252b40c2b5ce7be20d0481428b70f3ff589b0a8b8aaeb64c6bed701" + sha256: e5201c620eb2637dca88a756961fae4a7191bb30b4f2271e08b746405ffdf3fd url: "https://pub.dev" source: hosted - version: "4.10.2" + version: "4.10.5" webview_flutter_platform_interface: dependency: transitive description: @@ -1179,10 +1179,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.15.0" win32_registry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0fd184c..038432b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,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 # 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. -version: 1.2.6+2322 +version: 1.2.7+2323 environment: sdk: ^3.8.1