Merge pull request #2583 from ImranR98/dev

- Add support for zipped APKs - GitHub/HTML only for now (#983)
- Fix bug: Release asset download box crashing for track-only apps (#2567)
- Fix text alignment in app selection menu (#2577)
This commit is contained in:
Imran
2025-10-11 19:26:45 -04:00
committed by GitHub
36 changed files with 180 additions and 53 deletions

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "هذا الإشعار مطلوب للتحقق من التحديث في الخلفية (يمكن إخفاؤه في إعدادات نظام التشغيل)", "fgServiceNotice": "هذا الإشعار مطلوب للتحقق من التحديث في الخلفية (يمكن إخفاؤه في إعدادات نظام التشغيل)",
"excludeSecrets": "استبعاد الأسرار", "excludeSecrets": "استبعاد الأسرار",
"GHReqPrefix": "مثيل \"sky22333/hubproxy\" لطلبات GitHub", "GHReqPrefix": "مثيل \"sky22333/hubproxy\" لطلبات GitHub",
"includeZips": "تضمين ملفات ZIP",
"zippedApkFilterRegEx": "تصفية ملفات APK داخل ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "إزالة التطبيق؟", "one": "إزالة التطبيق؟",
"other": "إزالة التطبيقات؟" "other": "إزالة التطبيقات؟"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
"excludeSecrets": "Exclude secrets", "excludeSecrets": "Exclude secrets",
"GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests",
"includeZips": "Include ZIP files",
"zippedApkFilterRegEx": "Filter APKs inside ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Želite li ukloniti aplikaciju?", "one": "Želite li ukloniti aplikaciju?",
"other": "Želite li ukloniti aplikacije?" "other": "Želite li ukloniti aplikacije?"

View File

@@ -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)", "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", "excludeSecrets": "Exclude secrets",
"GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests",
"includeZips": "Include ZIP files",
"zippedApkFilterRegEx": "Filter APKs inside ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "¿Suprimeixo l'aplicació?", "one": "¿Suprimeixo l'aplicació?",
"other": "¿Suprimeixo les aplicacions?" "other": "¿Suprimeixo les aplicacions?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "Toto oznámení je nutné pro kontrolu aktualizací na pozadí (lze jej skrýt v nastavení systému)", "fgServiceNotice": "Toto oznámení je nutné pro kontrolu aktualizací na pozadí (lze jej skrýt v nastavení systému)",
"excludeSecrets": "Vyloučit tajemství", "excludeSecrets": "Vyloučit tajemství",
"GHReqPrefix": "instance 'sky22333/hubproxy' pro požadavky GitHubu", "GHReqPrefix": "instance 'sky22333/hubproxy' pro požadavky GitHubu",
"includeZips": "Zahrnout soubory ZIP",
"zippedApkFilterRegEx": "Filtrování souborů APK uvnitř ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Odstranit aplikaci?", "one": "Odstranit aplikaci?",
"other": "Odstranit aplikace?" "other": "Odstranit aplikace?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "Denne meddelelse er nødvendig for baggrundsopdateringskontrol (den kan skjules i OS-indstillingerne).", "fgServiceNotice": "Denne meddelelse er nødvendig for baggrundsopdateringskontrol (den kan skjules i OS-indstillingerne).",
"excludeSecrets": "Udeluk hemmeligheder", "excludeSecrets": "Udeluk hemmeligheder",
"GHReqPrefix": "'sky22333/hubproxy'-instans til GitHub-anmodninger", "GHReqPrefix": "'sky22333/hubproxy'-instans til GitHub-anmodninger",
"includeZips": "Inkluder ZIP-filer",
"zippedApkFilterRegEx": "Filtrer APK'er inde i ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Fjern app?", "one": "Fjern app?",
"other": "Fjern apps?" "other": "Fjern apps?"

View File

@@ -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)", "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", "excludeSecrets": "Geheimnisse ausschließen",
"GHReqPrefix": "sky22333/hubproxy'-Instanz für GitHub-Anfragen", "GHReqPrefix": "sky22333/hubproxy'-Instanz für GitHub-Anfragen",
"includeZips": "ZIP-Dateien einbeziehen",
"zippedApkFilterRegEx": "APKs in ZIP filtern",
"removeAppQuestion": { "removeAppQuestion": {
"one": "App entfernen?", "one": "App entfernen?",
"other": "Apps entfernen?" "other": "Apps entfernen?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
"excludeSecrets": "Exclude secrets", "excludeSecrets": "Exclude secrets",
"GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests",
"includeZips": "Include ZIP files",
"zippedApkFilterRegEx": "Filter APKs inside ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Forigi la aplikaĵon?", "one": "Forigi la aplikaĵon?",
"other": "Forigi la aplikaĵojn?" "other": "Forigi la aplikaĵojn?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
"excludeSecrets": "Exclude secrets", "excludeSecrets": "Exclude secrets",
"GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests",
"includeZips": "Include ZIP files",
"zippedApkFilterRegEx": "Filter APKs inside ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remove app?", "one": "Remove app?",
"other": "Remove apps?" "other": "Remove apps?"

View File

@@ -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).", "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", "excludeSecrets": "Excluir secretos",
"GHReqPrefix": "Instancia \"sky22333/hubproxy\" para las solicitudes de GitHub", "GHReqPrefix": "Instancia \"sky22333/hubproxy\" para las solicitudes de GitHub",
"includeZips": "Incluir archivos ZIP",
"zippedApkFilterRegEx": "Filtrar APKs dentro de ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "¿Eliminar aplicación?", "one": "¿Eliminar aplicación?",
"other": "¿Eliminar aplicaciones?" "other": "¿Eliminar aplicaciones?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
"excludeSecrets": "Exclude secrets", "excludeSecrets": "Exclude secrets",
"GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests",
"includeZips": "Include ZIP files",
"zippedApkFilterRegEx": "Filter APKs inside ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "برنامه حذف شود؟", "one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟" "other": "برنامه ها حذف شوند؟"

View File

@@ -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).", "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", "excludeSecrets": "Exclure les secrets",
"GHReqPrefix": "instance 'sky22333/hubproxy' pour les requêtes GitHub", "GHReqPrefix": "instance 'sky22333/hubproxy' pour les requêtes GitHub",
"includeZips": "Inclure les fichiers ZIP",
"zippedApkFilterRegEx": "Filtrer les APK à l'intérieur du ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Supprimer l'application ?", "one": "Supprimer l'application ?",
"other": "Supprimer les applications ?" "other": "Supprimer les applications ?"

View File

@@ -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ő).", "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", "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", "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": { "removeAppQuestion": {
"one": "Eltávolítja az alkalmazást?", "one": "Eltávolítja az alkalmazást?",
"other": "Eltávolítja az alkalmazásokat?" "other": "Eltávolítja az alkalmazásokat?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "Pemberitahuan ini diperlukan untuk pemeriksaan pembaruan latar belakang (dapat disembunyikan dalam pengaturan OS)", "fgServiceNotice": "Pemberitahuan ini diperlukan untuk pemeriksaan pembaruan latar belakang (dapat disembunyikan dalam pengaturan OS)",
"excludeSecrets": "Mengecualikan rahasia", "excludeSecrets": "Mengecualikan rahasia",
"GHReqPrefix": "Instance 'sky22333/hubproxy' untuk permintaan GitHub", "GHReqPrefix": "Instance 'sky22333/hubproxy' untuk permintaan GitHub",
"includeZips": "Menyertakan file ZIP",
"zippedApkFilterRegEx": "Saring APK di dalam ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Hapus aplikasi?", "one": "Hapus aplikasi?",
"other": "Hapus aplikasi?" "other": "Hapus aplikasi?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "Questa notifica è necessaria per il controllo degli aggiornamenti in background (può essere nascosta nelle impostazioni del sistema operativo).", "fgServiceNotice": "Questa notifica è necessaria per il controllo degli aggiornamenti in background (può essere nascosta nelle impostazioni del sistema operativo).",
"excludeSecrets": "Escludere i segreti", "excludeSecrets": "Escludere i segreti",
"GHReqPrefix": "istanza 'sky22333/hubproxy' per le richieste a GitHub", "GHReqPrefix": "istanza 'sky22333/hubproxy' per le richieste a GitHub",
"includeZips": "Includere file ZIP",
"zippedApkFilterRegEx": "Filtrare gli APK all'interno dello ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Rimuovere l'app?", "one": "Rimuovere l'app?",
"other": "Rimuovere le app?" "other": "Rimuovere le app?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "この通知は、バックグラウンドでアップデートを確認するために必要ですOSの設定で非表示にできます。", "fgServiceNotice": "この通知は、バックグラウンドでアップデートを確認するために必要ですOSの設定で非表示にできます。",
"excludeSecrets": "シークレットを除く", "excludeSecrets": "シークレットを除く",
"GHReqPrefix": "GitHub リクエスト用の 'sky22333/hubproxy' インスタンス", "GHReqPrefix": "GitHub リクエスト用の 'sky22333/hubproxy' インスタンス",
"includeZips": "ZIPファイルを含む",
"zippedApkFilterRegEx": "ZIP内のAPKをフィルタリングする",
"removeAppQuestion": { "removeAppQuestion": {
"one": "アプリを削除しますか?", "one": "アプリを削除しますか?",
"other": "アプリを削除しますか?" "other": "アプリを削除しますか?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "이 알림은 백그라운드 업데이트 확인에 필요합니다(OS 설정에서 숨길 수 있음).", "fgServiceNotice": "이 알림은 백그라운드 업데이트 확인에 필요합니다(OS 설정에서 숨길 수 있음).",
"excludeSecrets": "비밀 제외", "excludeSecrets": "비밀 제외",
"GHReqPrefix": "GitHub 요청을 위한 'sky22333/hubproxy' 인스턴스", "GHReqPrefix": "GitHub 요청을 위한 'sky22333/hubproxy' 인스턴스",
"includeZips": "ZIP 파일 포함",
"zippedApkFilterRegEx": "ZIP 내 APK 필터링",
"removeAppQuestion": { "removeAppQuestion": {
"one": "앱을 제거하시겠습니까?", "one": "앱을 제거하시겠습니까?",
"other": "앱을 제거하시겠습니까?" "other": "앱을 제거하시겠습니까?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
"excludeSecrets": "Exclude secrets", "excludeSecrets": "Exclude secrets",
"GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests",
"includeZips": "Include ZIP files",
"zippedApkFilterRegEx": "Filter APKs inside ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "ആപ്പ് നീക്കം ചെയ്യണോ?", "one": "ആപ്പ് നീക്കം ചെയ്യണോ?",
"other": "ആപ്പുകൾ നീക്കം ചെയ്യണോ?" "other": "ആപ്പുകൾ നീക്കം ചെയ്യണോ?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "Deze melding is nodig voor het controleren van updates op de achtergrond (kan worden verborgen in de OS-instellingen)", "fgServiceNotice": "Deze melding is nodig voor het controleren van updates op de achtergrond (kan worden verborgen in de OS-instellingen)",
"excludeSecrets": "Geheimen uitsluiten", "excludeSecrets": "Geheimen uitsluiten",
"GHReqPrefix": "'sky22333/hubproxy' instantie voor GitHub verzoeken", "GHReqPrefix": "'sky22333/hubproxy' instantie voor GitHub verzoeken",
"includeZips": "ZIP-bestanden opnemen",
"zippedApkFilterRegEx": "APK's filteren in ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "App verwijderen?", "one": "App verwijderen?",
"other": "Apps verwijderen?" "other": "Apps verwijderen?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "To powiadomienie jest wymagane do sprawdzania aktualizacji w tle (można je ukryć w ustawieniach systemu operacyjnego).", "fgServiceNotice": "To powiadomienie jest wymagane do sprawdzania aktualizacji w tle (można je ukryć w ustawieniach systemu operacyjnego).",
"excludeSecrets": "Wyklucz sekrety", "excludeSecrets": "Wyklucz sekrety",
"GHReqPrefix": "Instancja \"sky22333/hubproxy\" dla żądań GitHub", "GHReqPrefix": "Instancja \"sky22333/hubproxy\" dla żądań GitHub",
"includeZips": "Dołączanie plików ZIP",
"zippedApkFilterRegEx": "Filtrowanie plików APK wewnątrz ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Usunąć aplikację?", "one": "Usunąć aplikację?",
"few": "Usunąć aplikacje?", "few": "Usunąć aplikacje?",

View File

@@ -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)", "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", "excludeSecrets": "Excluir segredos",
"GHReqPrefix": "Instância \"sky22333/hubproxy\" para solicitações do GitHub", "GHReqPrefix": "Instância \"sky22333/hubproxy\" para solicitações do GitHub",
"includeZips": "Incluir arquivos ZIP",
"zippedApkFilterRegEx": "Filtrar APKs dentro do ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remover app?", "one": "Remover app?",
"other": "Remover apps?" "other": "Remover apps?"

View File

@@ -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)", "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", "excludeSecrets": "Excluir segredos",
"GHReqPrefix": "Instância 'sky22333/hubproxy' para pedidos de GitHub", "GHReqPrefix": "Instância 'sky22333/hubproxy' para pedidos de GitHub",
"includeZips": "Incluir ficheiros ZIP",
"zippedApkFilterRegEx": "Filtrar APKs dentro do ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remover aplicação?", "one": "Remover aplicação?",
"other": "Remover aplicações?" "other": "Remover aplicações?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "Это уведомление необходимо для фоновой проверки обновлений (оно может быть скрыто в настройках ОС)", "fgServiceNotice": "Это уведомление необходимо для фоновой проверки обновлений (оно может быть скрыто в настройках ОС)",
"excludeSecrets": "Исключить секреты", "excludeSecrets": "Исключить секреты",
"GHReqPrefix": "Экземпляр 'sky22333/hubproxy' для запросов на GitHub", "GHReqPrefix": "Экземпляр 'sky22333/hubproxy' для запросов на GitHub",
"includeZips": "Включить ZIP-файлы",
"zippedApkFilterRegEx": "Фильтр APK внутри ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Удалить приложение?", "one": "Удалить приложение?",
"other": "Удалить приложения?" "other": "Удалить приложения?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "Detta meddelande krävs för bakgrundsuppdateringskontroll (det kan döljas i OS-inställningarna)", "fgServiceNotice": "Detta meddelande krävs för bakgrundsuppdateringskontroll (det kan döljas i OS-inställningarna)",
"excludeSecrets": "Utesluta hemligheter", "excludeSecrets": "Utesluta hemligheter",
"GHReqPrefix": "Instansen \"sky22333/hubproxy\" för GitHub-förfrågningar", "GHReqPrefix": "Instansen \"sky22333/hubproxy\" för GitHub-förfrågningar",
"includeZips": "Inkludera ZIP-filer",
"zippedApkFilterRegEx": "Filtrera APK:er inuti ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Ta Bort App?", "one": "Ta Bort App?",
"other": "Ta Bort Appar?" "other": "Ta Bort Appar?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "Bu bildirim arka planda güncelleme kontrolü için gereklidir (işletim sistemi ayarlarından gizlenebilir)", "fgServiceNotice": "Bu bildirim arka planda güncelleme kontrolü için gereklidir (işletim sistemi ayarlarından gizlenebilir)",
"excludeSecrets": "Sırları hariç tut", "excludeSecrets": "Sırları hariç tut",
"GHReqPrefix": "GitHub istekleri için 'sky22333/hubproxy' örneği", "GHReqPrefix": "GitHub istekleri için 'sky22333/hubproxy' örneği",
"includeZips": "ZIP dosyalarını dahil edin",
"zippedApkFilterRegEx": "ZIP içindeki APK'ları filtreleme",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Uygulamayı Kaldır?", "one": "Uygulamayı Kaldır?",
"other": "Uygulamaları Kaldır?" "other": "Uygulamaları Kaldır?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "Це сповіщення необхідне для фонової перевірки оновлень (його можна приховати в налаштуваннях ОС)", "fgServiceNotice": "Це сповіщення необхідне для фонової перевірки оновлень (його можна приховати в налаштуваннях ОС)",
"excludeSecrets": "Виключити секрети", "excludeSecrets": "Виключити секрети",
"GHReqPrefix": "екземпляр 'sky22333/hubproxy' для запитів на GitHub", "GHReqPrefix": "екземпляр 'sky22333/hubproxy' для запитів на GitHub",
"includeZips": "Додайте ZIP-файли",
"zippedApkFilterRegEx": "Фільтруйте APK-файли всередині ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Видалити застосунок?", "one": "Видалити застосунок?",
"other": "Видалити застосунки?" "other": "Видалити застосунки?"

View File

@@ -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).", "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", "excludeSecrets": "Loại trừ thông tin bí mật",
"GHReqPrefix": "Thực thể 'sky22333/hubproxy' cho các yêu cầu GitHub", "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": { "removeAppQuestion": {
"one": "Gỡ ứng dụng?", "one": "Gỡ ứng dụng?",
"other": "Gỡ ứng dụng?" "other": "Gỡ ứng dụng?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)", "fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
"excludeSecrets": "Exclude secrets", "excludeSecrets": "Exclude secrets",
"GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests", "GHReqPrefix": "'sky22333/hubproxy' instance for GitHub requests",
"includeZips": "Include ZIP files",
"zippedApkFilterRegEx": "Filter APKs inside ZIP",
"removeAppQuestion": { "removeAppQuestion": {
"one": "移除應用程式?", "one": "移除應用程式?",
"other": "移除應用程式?" "other": "移除應用程式?"

View File

@@ -337,6 +337,8 @@
"fgServiceNotice": "后台检查更新时需要此通知(可在操作系统设置中隐藏)", "fgServiceNotice": "后台检查更新时需要此通知(可在操作系统设置中隐藏)",
"excludeSecrets": "排除机密", "excludeSecrets": "排除机密",
"GHReqPrefix": "用于 GitHub 请求的 \"sky22333/hubproxy \"实例", "GHReqPrefix": "用于 GitHub 请求的 \"sky22333/hubproxy \"实例",
"includeZips": "包含 ZIP 文件",
"zippedApkFilterRegEx": "过滤 ZIP 内的 APK",
"removeAppQuestion": { "removeAppQuestion": {
"one": "是否删除应用?", "one": "是否删除应用?",
"other": "是否删除应用?" "other": "是否删除应用?"

View File

@@ -18,6 +18,7 @@ class GitHub extends AppSource {
appIdInferIsOptional = true; appIdInferIsOptional = true;
showReleaseDateAsVersionToggle = true; showReleaseDateAsVersionToggle = true;
this.hostChanged = hostChanged; this.hostChanged = hostChanged;
allowIncludeZips = true;
sourceConfigSettingFormItems = [ sourceConfigSettingFormItems = [
GeneratedFormTextField( GeneratedFormTextField(
@@ -370,6 +371,7 @@ class GitHub extends AppSource {
additionalSettings['useLatestAssetDateAsReleaseDate'] == true; additionalSettings['useLatestAssetDateAsReleaseDate'] == true;
String sortMethod = String sortMethod =
additionalSettings['sortMethodChoice'] ?? 'smartname-datefallback'; additionalSettings['sortMethodChoice'] ?? 'smartname-datefallback';
bool includeZips = additionalSettings['includeZips'] == true;
dynamic latestRelease; dynamic latestRelease;
if (verifyLatestTag) { if (verifyLatestTag) {
var temp = requestUrl.split('?'); var temp = requestUrl.split('?');
@@ -402,7 +404,8 @@ class GitHub extends AppSource {
findReleaseAssetUrls(dynamic release) => findReleaseAssetUrls(dynamic release) =>
(release['assets'] as List<dynamic>?)?.map((e) { (release['assets'] as List<dynamic>?)?.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['browser_download_url'] ?? e['url'])
: (e['url'] ?? e['browser_download_url']); : (e['url'] ?? e['browser_download_url']);
url = undoGHProxyMod(url, sourceConfigSettingValues); url = undoGHProxyMod(url, sourceConfigSettingValues);
@@ -542,14 +545,13 @@ class GitHub extends AppSource {
List<MapEntry<String, String>> allAssetUrls = allAssetsWithUrls List<MapEntry<String, String>> allAssetUrls = allAssetsWithUrls
.map((e) => e['final_url'] as MapEntry<String, String>) .map((e) => e['final_url'] as MapEntry<String, String>)
.toList(); .toList();
var apkAssetsWithUrls = allAssetsWithUrls var apkAssetsWithUrls = allAssetsWithUrls.where((element) {
.where( var ext = (element['final_url'] as MapEntry<String, String>).key
(element) => (element['final_url'] as MapEntry<String, String>)
.key
.toLowerCase() .toLowerCase()
.endsWith('.apk'), .split('.')
) .last;
.toList(); return ext == 'apk' || (includeZips && ext == 'zip');
}).toList();
var filteredApkUrls = filterApks( var filteredApkUrls = filterApks(
apkAssetsWithUrls apkAssetsWithUrls

View File

@@ -203,11 +203,11 @@ class AddAppPageState extends State<AddAppPage> {
notificationsProvider: notificationsProvider, notificationsProvider: notificationsProvider,
); );
DownloadedApk? downloadedFile; DownloadedApk? downloadedFile;
DownloadedXApkDir? downloadedDir; DownloadedDir? downloadedDir;
if (downloadedArtifact is DownloadedApk) { if (downloadedArtifact is DownloadedApk) {
downloadedFile = downloadedArtifact; downloadedFile = downloadedArtifact;
} else { } else {
downloadedDir = downloadedArtifact as DownloadedXApkDir; downloadedDir = downloadedArtifact as DownloadedDir;
} }
app.id = downloadedFile?.appId ?? downloadedDir!.appId; app.id = downloadedFile?.appId ?? downloadedDir!.appId;
} }

View File

@@ -1010,6 +1010,7 @@ class AppsPageState extends State<AppsPage> {
selectedApps.where((element) => element.pinned).isEmpty selectedApps.where((element) => element.pinned).isEmpty
? tr('pinToTop') ? tr('pinToTop')
: tr('unpinFromTop'), : tr('unpinFromTop'),
textAlign: TextAlign.center,
), ),
), ),
const Divider(), const Divider(),
@@ -1026,7 +1027,10 @@ class AppsPageState extends State<AppsPage> {
); );
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Text(tr('shareSelectedAppURLs')), child: Text(
tr('shareSelectedAppURLs'),
textAlign: TextAlign.center,
),
), ),
const Divider(), const Divider(),
TextButton( TextButton(
@@ -1043,7 +1047,10 @@ class AppsPageState extends State<AppsPage> {
subject: 'Obtainium - ${tr('appsString')}', subject: 'Obtainium - ${tr('appsString')}',
); );
}, },
child: Text(tr('shareAppConfigLinks')), child: Text(
tr('shareAppConfigLinks'),
textAlign: TextAlign.center,
),
), ),
const Divider(), const Divider(),
TextButton( TextButton(
@@ -1069,7 +1076,10 @@ class AppsPageState extends State<AppsPage> {
fileNameOverrides: ['$fn.json'], fileNameOverrides: ['$fn.json'],
); );
}, },
child: Text('${tr('share')} - ${tr('obtainiumExport')}'), child: Text(
'${tr('share')} - ${tr('obtainiumExport')}',
textAlign: TextAlign.center,
),
), ),
const Divider(), const Divider(),
TextButton( TextButton(
@@ -1093,6 +1103,7 @@ class AppsPageState extends State<AppsPage> {
'downloadX', 'downloadX',
args: [lowerCaseIfEnglish(tr('releaseAsset'))], args: [lowerCaseIfEnglish(tr('releaseAsset'))],
), ),
textAlign: TextAlign.center,
), ),
), ),
const Divider(), const Divider(),
@@ -1100,7 +1111,10 @@ class AppsPageState extends State<AppsPage> {
onPressed: appsProvider.areDownloadsRunning() onPressed: appsProvider.areDownloadsRunning()
? null ? null
: showMassMarkDialog, : showMassMarkDialog,
child: Text(tr('markSelectedAppsUpdated')), child: Text(
tr('markSelectedAppsUpdated'),
textAlign: TextAlign.center,
),
), ),
], ],
), ),

View File

@@ -62,11 +62,14 @@ class DownloadedApk {
DownloadedApk(this.appId, this.file); DownloadedApk(this.appId, this.file);
} }
class DownloadedXApkDir { enum DownloadedDirType { XAPK, ZIP }
class DownloadedDir {
String appId; String appId;
File file; File file;
Directory extracted; Directory extracted;
DownloadedXApkDir(this.appId, this.file, this.extracted); DownloadedDirType type;
DownloadedDir(this.appId, this.file, this.extracted, this.type);
} }
List<String> generateStandardVersionRegExStrings() { List<String> generateStandardVersionRegExStrings() {
@@ -664,17 +667,18 @@ class AppsProvider with ChangeNotifier {
} }
PackageInfo? newInfo; PackageInfo? newInfo;
var isAPK = downloadedFile.path.toLowerCase().endsWith('.apk'); var isAPK = downloadedFile.path.toLowerCase().endsWith('.apk');
Directory? xapkDir; var isXAPK = downloadedFile.path.toLowerCase().endsWith('.xapk');
Directory? apkDir;
if (isAPK) { if (isAPK) {
newInfo = await pm.getPackageArchiveInfo( newInfo = await pm.getPackageArchiveInfo(
archiveFilePath: downloadedFile.path, archiveFilePath: downloadedFile.path,
); );
} else { } else {
// Assume XAPK // Assume XAPK or ZIP
String xapkDirPath = '${downloadedFile.path}-dir'; String apkDirPath = '${downloadedFile.path}-dir';
await unzipFile(downloadedFile.path, '${downloadedFile.path}-dir'); await unzipFile(downloadedFile.path, '${downloadedFile.path}-dir');
xapkDir = Directory(xapkDirPath); apkDir = Directory(apkDirPath);
var apks = xapkDir var apks = apkDir
.listSync() .listSync()
.where((e) => e.path.toLowerCase().endsWith('.apk')) .where((e) => e.path.toLowerCase().endsWith('.apk'))
.toList(); .toList();
@@ -691,6 +695,22 @@ class AppsProvider with ChangeNotifier {
apks = [temp!, ...apks]; 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++) { for (var i = 0; i < apks.length; i++) {
try { try {
newInfo = await pm.getPackageArchiveInfo( newInfo = await pm.getPackageArchiveInfo(
@@ -728,7 +748,12 @@ class AppsProvider with ChangeNotifier {
if (isAPK) { if (isAPK) {
return DownloadedApk(app.id, downloadedFile); return DownloadedApk(app.id, downloadedFile);
} else { } else {
return DownloadedXApkDir(app.id, downloadedFile, xapkDir!); return DownloadedDir(
app.id,
downloadedFile,
apkDir!,
isXAPK ? DownloadedDirType.XAPK : DownloadedDirType.ZIP,
);
} }
} finally { } finally {
notificationsProvider?.cancel(notifId); notificationsProvider?.cancel(notifId);
@@ -826,15 +851,16 @@ class AppsProvider with ChangeNotifier {
); );
} }
Future<bool> installXApkDir( Future<bool> installApkDir(
DownloadedXApkDir dir, DownloadedDir dir,
BuildContext? firstTimeWithContext, { BuildContext? firstTimeWithContext, {
bool needsBGWorkaround = false, bool needsBGWorkaround = false,
bool shizukuPretendToBeGooglePlay = false, bool shizukuPretendToBeGooglePlay = false,
}) async { }) 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 // 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 // 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; var somethingInstalled = false;
try { try {
MultiAppMultiError errors = MultiAppMultiError(); MultiAppMultiError errors = MultiAppMultiError();
@@ -863,7 +889,7 @@ class AppsProvider with ChangeNotifier {
} }
try { try {
await installApk( var wasInstalled = await installApk(
DownloadedApk(dir.appId, APKFiles[0]), DownloadedApk(dir.appId, APKFiles[0]),
firstTimeWithContext, firstTimeWithContext,
needsBGWorkaround: needsBGWorkaround, needsBGWorkaround: needsBGWorkaround,
@@ -872,10 +898,10 @@ class AppsProvider with ChangeNotifier {
1, 1,
).map((a) => DownloadedApk(dir.appId, a)).toList(), ).map((a) => DownloadedApk(dir.appId, a)).toList(),
); );
somethingInstalled = true; somethingInstalled = somethingInstalled || wasInstalled;
dir.file.delete(recursive: true); dir.file.delete(recursive: true);
} catch (e) { } 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); errors.add(dir.appId, e, appName: apps[dir.appId]?.name);
} }
if (errors.idsByErrorString.isNotEmpty) { if (errors.idsByErrorString.isNotEmpty) {
@@ -1148,7 +1174,7 @@ class AppsProvider with ChangeNotifier {
String id, String id,
bool willBeSilent, bool willBeSilent,
DownloadedApk? downloadedFile, DownloadedApk? downloadedFile,
DownloadedXApkDir? downloadedDir, DownloadedDir? downloadedDir,
) async { ) async {
apps[id]?.downloadProgress = -1; apps[id]?.downloadProgress = -1;
notifyListeners(); notifyListeners();
@@ -1183,14 +1209,14 @@ class AppsProvider with ChangeNotifier {
} else { } else {
if (needBGWorkaround) { if (needBGWorkaround) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
installXApkDir( installApkDir(
downloadedDir!, downloadedDir!,
contextIfNewInstall, contextIfNewInstall,
needsBGWorkaround: true, needsBGWorkaround: true,
); );
} else { } else {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
sayInstalled = await installXApkDir( sayInstalled = await installApkDir(
downloadedDir!, downloadedDir!,
contextIfNewInstall, contextIfNewInstall,
shizukuPretendToBeGooglePlay: shizukuPretendToBeGooglePlay, shizukuPretendToBeGooglePlay: shizukuPretendToBeGooglePlay,
@@ -1227,7 +1253,7 @@ class AppsProvider with ChangeNotifier {
}) async { }) async {
bool willBeSilent = false; bool willBeSilent = false;
DownloadedApk? downloadedFile; DownloadedApk? downloadedFile;
DownloadedXApkDir? downloadedDir; DownloadedDir? downloadedDir;
try { try {
var downloadedArtifact = var downloadedArtifact =
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
@@ -1240,7 +1266,7 @@ class AppsProvider with ChangeNotifier {
if (downloadedArtifact is DownloadedApk) { if (downloadedArtifact is DownloadedApk) {
downloadedFile = downloadedArtifact; downloadedFile = downloadedArtifact;
} else { } else {
downloadedDir = downloadedArtifact as DownloadedXApkDir; downloadedDir = downloadedArtifact as DownloadedDir;
} }
id = downloadedFile?.appId ?? downloadedDir!.appId; id = downloadedFile?.appId ?? downloadedDir!.appId;
willBeSilent = await canInstallSilently(apps[id]!.app); willBeSilent = await canInstallSilently(apps[id]!.app);
@@ -1292,7 +1318,7 @@ class AppsProvider with ChangeNotifier {
res['id'] as String, res['id'] as String,
res['willBeSilent'] as bool, res['willBeSilent'] as bool,
res['downloadedFile'] as DownloadedApk?, res['downloadedFile'] as DownloadedApk?,
res['downloadedDir'] as DownloadedXApkDir?, res['downloadedDir'] as DownloadedDir?,
); );
} catch (e) { } catch (e) {
var id = res['id'] as String; var id = res['id'] as String;
@@ -1323,6 +1349,7 @@ class AppsProvider with ChangeNotifier {
MapEntry<String, String>? fileUrl; MapEntry<String, String>? fileUrl;
var refreshBeforeDownload = var refreshBeforeDownload =
apps[id]!.app.additionalSettings['refreshBeforeDownload'] == true || apps[id]!.app.additionalSettings['refreshBeforeDownload'] == true ||
apps[id]!.app.apkUrls.isNotEmpty &&
apps[id]!.app.apkUrls.first.value == 'placeholder'; apps[id]!.app.apkUrls.first.value == 'placeholder';
if (refreshBeforeDownload) { if (refreshBeforeDownload) {
await checkUpdate(apps[id]!.app.id); await checkUpdate(apps[id]!.app.id);

View File

@@ -636,6 +636,7 @@ abstract class AppSource {
bool versionDetectionDisallowed = false; bool versionDetectionDisallowed = false;
List<String> excludeCommonSettingKeys = []; List<String> excludeCommonSettingKeys = [];
bool urlsAlwaysHaveExtension = false; bool urlsAlwaysHaveExtension = false;
bool allowIncludeZips = false;
AppSource() { AppSource() {
name = runtimeType.toString(); 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<List<GeneratedFormItem>> get combinedAppSpecificSettingFormItems { List<List<GeneratedFormItem>> get combinedAppSpecificSettingFormItems {
if (showReleaseDateAsVersionToggle == true) { if (showReleaseDateAsVersionToggle == true) {
if (additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly if (additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
@@ -878,6 +879,32 @@ abstract class AppSource {
) )
.where((e) => e.isNotEmpty) .where((e) => e.isNotEmpty)
.toList(); .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) { if (versionDetectionDisallowed) {
overrideAdditionalAppSpecificSourceAgnosticSettingSwitch( overrideAdditionalAppSpecificSourceAgnosticSettingSwitch(
'versionDetection', 'versionDetection',
@@ -893,6 +920,7 @@ abstract class AppSource {
return [ return [
...additionalSourceAppSpecificSettingFormItems, ...additionalSourceAppSpecificSettingFormItems,
...additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly, ...additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly,
...moreConditionalItems,
]; ];
} }

View File

@@ -40,10 +40,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: animations name: animations
sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb sha256: a8031b276f0a7986ac907195f10ca7cd04ecf2a8a566bd6dbe03018a9b02b427
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.11" version: "2.1.0"
app_links: app_links:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -482,10 +482,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: b0694b7fb1689b0e6cc193b3f1fcac6423c4f93c74fb20b806c6b6f196db0c31 sha256: c2fe1001710127dfa7da89977a08d591398370d099aacdaa6d44da7eb14b8476
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.30" version: "2.0.31"
flutter_test: flutter_test:
dependency: transitive dependency: transitive
description: flutter description: flutter
@@ -676,10 +676,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "993381400e94d18469750e5b9dcb8206f15bc09f9da86b9e44a9b0092a0066db" sha256: "3b4c1fc3aa55ddc9cd4aa6759984330d5c8e66aa7702a6223c61540dc6380c37"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.18" version: "2.2.19"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
@@ -852,10 +852,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
sha256: bd14436108211b0d4ee5038689a56d4ae3620fd72fd6036e113bf1345bc74d9e sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.13" version: "2.4.15"
shared_preferences_foundation: shared_preferences_foundation:
dependency: transitive dependency: transitive
description: description:
@@ -1059,10 +1059,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: "199bc33e746088546a39cc5f36bac5a278c5e53b40cb3196f99e7345fdcfae6b" sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.22" version: "6.3.24"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
@@ -1155,10 +1155,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: "3c4eb4fcc252b40c2b5ce7be20d0481428b70f3ff589b0a8b8aaeb64c6bed701" sha256: e5201c620eb2637dca88a756961fae4a7191bb30b4f2271e08b746405ffdf3fd
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.10.2" version: "4.10.5"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -1179,10 +1179,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.14.0" version: "5.15.0"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:

View File

@@ -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 # 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.2.6+2322 version: 1.2.7+2323
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1