Compare commits

...

41 Commits

Author SHA1 Message Date
Imran
d21feaaf74 Merge pull request #2574 from neardaniel-pls/main
Refine Portuguese Translation
2025-10-11 19:27:17 -04:00
Imran
c0c0e2a5c5 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)
2025-10-11 19:26:45 -04:00
Imran Remtulla
60e7bba5d9 Flutter upgrade + increment version 2025-10-11 19:25:55 -04:00
Imran Remtulla
6654406c81 Fix text alignment in app selection menu (#2577) 2025-10-11 18:33:30 -04:00
Imran Remtulla
f5cbbfcace Fix bug: Release asset download box crashing for track-only apps (#2567) 2025-10-11 18:30:54 -04:00
Imran Remtulla
8bdc0f8d12 Add support for zipped APKs - GitHub/HTML only for now (#983) 2025-10-11 18:29:59 -04:00
NearDaniel
a980b00600 Merge branch 'ImranR98:main' into main 2025-09-30 19:02:38 +01:00
Near
7a385843f9 chore(i18n): refine Portuguese translation
This commit updates the Portuguese translation (`pt.json`) to improve consistency and correctness.

The primary change involves replacing most instances of "aplicativo" with "aplicação" to better align with European Portuguese terminology. Other minor grammatical and wording adjustments have also been made throughout the file.
2025-09-29 21:32:03 +01:00
Imran
7cb9ca128b Merge pull request #2560 from ImranR98/dev
Stop sending GitHub token if a "hubproxy" instance is in use
2025-09-27 02:53:42 -04:00
Imran
ced7b13777 Merge pull request #2555 from summoner001/main
Update hu.json
2025-09-27 02:53:12 -04:00
Imran Remtulla
41f37f6d32 Upgrade packages, increment version 2025-09-27 02:49:58 -04:00
Imran Remtulla
fef0120732 Minor form validation fix 2025-09-27 02:46:27 -04:00
Imran Remtulla
6830eb64d0 Stop sending GitHub token if a "hubproxy" instance is in use 2025-09-27 02:40:03 -04:00
summoner
b4506c4c3f Update hu.json
Fix translation
2025-09-22 16:54:38 +02:00
Imran
3af185b0f8 Merge pull request #2552 from aminosred25/aminosred25-patch-1
Try to handle response body as a JSON first before handling it as a p…
2025-09-21 16:56:50 -04:00
aminosred25
bfbb2710b6 Try to handle response body as a JSON first before handling it as a plain text 2025-09-21 22:33:57 +02:00
Imran
c81c3c5ea7 Merge pull request #2551 from ImranR98/dev
- Add 'sky22333/hubproxy' support for GitHub (#2513)
- Fix error in Vivo Store search when no result found (#2494)
- Correct a log string (#2519)
- Loose auto-filtering for farsroid.com (#2441)
- Re-enable auto-filtering for farsroid.com w/ toggle to disable it (#2441)
- Enable long-press and double-tap gestures on app icon in apps list (#2444, #2448)
2025-09-21 14:14:37 -04:00
Imran Remtulla
7391e9a94d Upgrade Flutter + packages, increment version 2025-09-21 14:11:40 -04:00
Imran Remtulla
04633131e7 Enable long-press and double-tap gestures on app icon in apps list (#2444, #2448) 2025-09-21 13:37:57 -04:00
Imran Remtulla
7bc818fbfa Re-enable auto-filtering for farsroid.com w/ toggle to disable it (#2441) 2025-09-21 13:01:59 -04:00
Imran Remtulla
4afcd92510 Loose auto-filtering for farsroid.com (#2441) 2025-09-21 12:51:06 -04:00
Imran Remtulla
0784f83792 Correct a log string (#2519) 2025-09-21 12:34:07 -04:00
Imran Remtulla
a6608f1461 Fix error in Vivo Store search when no result found (#2494) 2025-09-21 12:28:02 -04:00
Imran Remtulla
08d9c4daaa Add 'sky22333/hubproxy' support for GitHub (#2513) 2025-09-21 12:16:58 -04:00
Imran
225362d351 Merge pull request #2539 from neardaniel-pls/main
Refined translation terms
2025-09-19 23:13:36 -04:00
Imran
089bf97ff2 Merge pull request #2545 from kvak54/rustore-patch
Restore RuStore support through replace URL of API.
2025-09-19 23:13:24 -04:00
kvak54
123cd5b130 Restore RuStore support through replace URL of API.
If the application is published as an AAB,
then the old one produced an unusable file (one of split files).
The new one provide a monolithic file.
2025-09-14 21:07:32 +07:00
Near
bc24f008fa fixed spacing 2025-09-13 22:41:20 +01:00
Near
472ec4e400 refine translation terms 2025-09-13 22:37:19 +01:00
Imran
a56069e881 Merge pull request #2537 from ImranR98/dev
Fix versionCode issue (#2505) + Flutter upgrades
2025-09-12 15:16:01 -04:00
Imran Remtulla
a34e45363b Fix versionCode issue (#2505) + Flutter upgrades 2025-09-12 15:13:20 -04:00
Imran
e1a89f5f67 Merge pull request #2531 from ar-lex/feature/3rd-party-fdroid-per-app-author
Add per-application author parsing for third-party F-Droid repos (#2527)
2025-09-12 14:44:10 -04:00
Imran
f7f6e0cde7 Merge pull request #2532 from ar-lex/feature/3rd-party-fdroid-changelog
Support changelog field for third-party F-Droid repos (#2528)
2025-09-12 14:44:04 -04:00
Alexey Arutyunov
5a58522bf6 Support changelog field for third-party F-Droid repos (#2528) 2025-09-12 20:39:23 +02:00
Alexey Arutyunov
9347af2090 Add per-application author parsing for third-party F-Droid repos (#2527) 2025-09-12 20:33:49 +02:00
Imran
b294e57367 Merge pull request #2530 from ar-lex/feature/3rd-party-fdroid-suggested-version
Implement suggested version code for third-party F-Droid repos (#2524)
2025-09-12 14:23:30 -04:00
Imran
4234e16432 Merge pull request #2533 from solokot/main
Translation: update ru.json
2025-09-12 14:21:10 -04:00
Imran
a64f2e1af7 Merge pull request #2522 from bluefly000/japanese-translation
Update ja.json
2025-09-12 14:20:50 -04:00
solokot
926462407c Translation: update ru.json
Fix machine translation
2025-09-12 09:57:50 +03:00
Alexey Arutyunov
1b499700fd Implement suggested version code for third-party F-Droid repos (#2524)
Enable it by default for both official and third-party F-Droid sources.

Reuse trySelectingSuggestedVersionCode label from official F-Droid repository source.
2025-09-11 23:34:28 +02:00
bluefly000
930a4b411e Update ja.json 2025-09-06 18:54:11 +09:00
49 changed files with 666 additions and 285 deletions

View File

@@ -1,6 +1,7 @@
import java.io.FileInputStream
import java.util.Properties
import com.android.build.api.variant.FilterConfiguration.FilterType.*
import com.android.build.gradle.internal.api.ApkVariantOutputImpl
plugins {
id("com.android.application")
@@ -86,18 +87,17 @@ android {
val abiCodes = mapOf("x86_64" to 1, "armeabi-v7a" to 2, "arm64-v8a" to 3)
androidComponents {
onVariants { variant ->
variant.outputs.forEach { output ->
val name = output.filters.find { it.filterType == ABI }?.identifier
val baseAbiCode = abiCodes[name] ?: 0
if (baseAbiCode != null) {
output.versionCode.set(baseAbiCode + ((output.versionCode.get() ?: 0) * 10))
}
android.applicationVariants.configureEach {
val variant = this
variant.outputs.forEach { output ->
val abiVersionCode = abiCodes[output.filters.find { it.filterType == "ABI" }?.identifier]
if (abiVersionCode != null) {
(output as ApkVariantOutputImpl).versionCodeOverride = variant.versionCode * 10 + abiVersionCode
}
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
}

View File

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

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Usa el servei d'Obtainium en primer pla per comprovar les actualitzacions (és més fiable però consumeix més bateria)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Použít službu v popředí pro kontrolu aktualizací (spolehlivější, spotřebovává více energie)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Brug en forgrundstjeneste til opdateringskontrol (mere pålidelig, bruger mere strøm)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Aktualisierungsprüfung im Vordergrund durchführen (zuverlässiger, verbraucht mehr Strom)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Usar un servicio en primer plano para comprobar las actualizaciones (más fiable, consume más energía).",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
"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": "برنامه ها حذف شوند؟"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Utiliser un service de premier plan pour la vérification des mises à jour (plus fiable, consomme plus d'énergie)",
"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 ?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Előtér-szolgáltatás használata a frissítések ellenőrzéséhez (megbízhatóbb, de több energiát fogyaszt)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Gunakan layanan latar depan untuk pemeriksaan pembaruan (lebih dapat diandalkan, menghabiskan lebih banyak daya)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Utilizzare un servizio in primo piano per il controllo degli aggiornamenti (più affidabile, consuma più energia)",
"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?"

View File

@@ -335,7 +335,10 @@
"foregroundService": "Obtainium フォアグラウンドサービス",
"foregroundServiceExplanation": "アップデート確認にフォアグラウンドサービスを使用する(より信頼性が高いが、より電力を消費する)",
"fgServiceNotice": "この通知は、バックグラウンドでアップデートを確認するために必要ですOSの設定で非表示にできます。",
"excludeSecrets": "秘密を除く",
"excludeSecrets": "シークレットを除く",
"GHReqPrefix": "GitHub リクエスト用の 'sky22333/hubproxy' インスタンス",
"includeZips": "ZIPファイルを含む",
"zippedApkFilterRegEx": "ZIP内のAPKをフィルタリングする",
"removeAppQuestion": {
"one": "アプリを削除しますか?",
"other": "アプリを削除しますか?"

View File

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

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
"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": "ആപ്പുകൾ നീക്കം ചെയ്യണോ?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Gebruik een voorgronddienst voor het controleren van updates (betrouwbaarder, verbruikt meer stroom)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Używanie usługi pierwszoplanowej do sprawdzania aktualizacji (bardziej niezawodne, zużywa więcej energii)",
"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?",

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Usar um serviço em primeiro plano para verificação de atualizações (mais confiável, consome mais energia)",
"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?"

View File

@@ -3,8 +3,8 @@
"noReleaseFound": "Não foi possível encontrar uma versão adequada",
"noVersionFound": "Não foi possível encontrar uma versão",
"urlMatchesNoSource": "A URL não corresponde a uma fonte conhecida",
"cantInstallOlderVersion": "Não é permitido instalar uma versão anterior de um aplicativo",
"appIdMismatch": "ID do pacote baixado não é igual ao ID do aplicativo instalado",
"cantInstallOlderVersion": "Não é permitido instalar uma versão anterior de uma aplicação",
"appIdMismatch": "O ID do pacote descarregado não é igual ao ID da aplicação instalada",
"functionNotImplemented": "Esta classe não implementou essa função",
"placeholder": "Espaço reservado",
"someErrors": "Alguns erros ocorreram",
@@ -30,15 +30,15 @@
"wrongArgNum": "Número de argumentos errado",
"xIsTrackOnly": "{} é 'Apenas monitorar'",
"source": "Fonte",
"app": "Aplicativo",
"appsFromSourceAreTrackOnly": "Os aplicativos desta fonte são 'Apenas monitorar'.",
"app": "Aplicação",
"appsFromSourceAreTrackOnly": "As aplicações desta fonte são 'Apenas monitorar'.",
"youPickedTrackOnly": "Você selecionou a opção 'Apenas monitorar'.",
"trackOnlyAppDescription": "As atualizações desse aplicativo serão monitoradas, mas o Obtainium não poderá baixá-lo ou instalá-lo.",
"trackOnlyAppDescription": "As atualizações desta aplicação serão monitorizadas, mas o Obtainium não poderá descarregá-la ou instalá-la.",
"cancelled": "Cancelado",
"appAlreadyAdded": "Aplicativo já adicionado",
"alreadyUpToDateQuestion": "Aplicativo já foi atualizado?",
"addApp": "Adicionar aplicativo",
"appSourceURL": "URL de origem do aplicativo",
"appAlreadyAdded": "Aplicação já adicionada",
"alreadyUpToDateQuestion": "Aplicação já foi atualizada?",
"addApp": "Adicionar aplicação",
"appSourceURL": "URL de origem da aplicação",
"error": "Erro",
"add": "Adicionar",
"searchSomeSourcesLabel": "Procurar (apenas algumas fontes)",
@@ -47,9 +47,9 @@
"supportedSources": "Fontes compatíveis",
"trackOnlyInBrackets": "(apenas monitorar)",
"searchableInBrackets": "(pesquisável)",
"appsString": "Aplicativos",
"noApps": "Não há aplicativos",
"noAppsForFilter": "Sem aplicativos para filtrar",
"appsString": "Aplicações",
"noApps": "Não há aplicações",
"noAppsForFilter": "Sem aplicações para filtrar",
"byX": "Por {}",
"percentProgress": "Progresso: {}%",
"pleaseWait": "Por favor, espere",
@@ -59,35 +59,35 @@
"selectAll": "Selecionar todos",
"deselectX": "Deselecionar {}",
"xWillBeRemovedButRemainInstalled": "{} será removido do Obtainium mais permanecerá instalado no dispositivo.",
"removeSelectedAppsQuestion": "Remover aplicativos selecionados?",
"removeSelectedApps": "Remover aplicativos selecionados",
"removeSelectedAppsQuestion": "Remover aplicações selecionadas?",
"removeSelectedApps": "Remover aplicações selecionadas",
"updateX": "Atualizar {}",
"installX": "Instalar {}",
"markXTrackOnlyAsUpdated": "Marcar {}\n(Apenas monitorar)\ncomo Atualizado",
"changeX": "Mudar {}",
"installUpdateApps": "Instalar/Atualizar aplicativos",
"installUpdateSelectedApps": "Instalar/Atualizar aplicativos selecionados",
"markXSelectedAppsAsUpdated": "Marcar {} aplicativos selecionados como atualizados?",
"installUpdateApps": "Instalar/Atualizar aplicações",
"installUpdateSelectedApps": "Instalar/Atualizar aplicações selecionadas",
"markXSelectedAppsAsUpdated": "Marcar {} aplicações selecionadas como atualizadas?",
"no": "Não",
"yes": "Sim",
"markSelectedAppsUpdated": "Marcar aplicativos selecionados como Atualizados",
"markSelectedAppsUpdated": "Marcar aplicações selecionadas como Atualizadas",
"pinToTop": "Fixar no topo",
"unpinFromTop": "Desafixar do topo",
"resetInstallStatusForSelectedAppsQuestion": "Reiniciar status de instalação nos aplicativos selecionados?",
"installStatusOfXWillBeResetExplanation": "O status de instalação de todos os aplicativos selecionados será reiniciado.\n\nIsso pode ajudar quando uma versão de um aplicativo mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.",
"resetInstallStatusForSelectedAppsQuestion": "Reiniciar o estado de instalação das aplicações selecionadas?",
"installStatusOfXWillBeResetExplanation": "O estado de instalação de todas as aplicações selecionadas será reiniciado.\n\nIsto pode ajudar quando uma versão de uma aplicação mostrada no Obtainium está incorreta devido a falhas na atualização ou outros problemas.",
"customLinkMessage": "Esses links funcionam em dispositivos com o Obtainium instalado",
"shareAppConfigLinks": "Compartilhar configuração do aplicativo como link HTML",
"shareSelectedAppURLs": "Compartilhar URLs de aplicativos selecionados",
"shareAppConfigLinks": "Partilhar a configuração da aplicação como um link HTML",
"shareSelectedAppURLs": "Partilhar URLs de aplicações selecionadas",
"resetInstallStatus": "Reiniciar status de instalação",
"more": "Mais",
"removeOutdatedFilter": "Remover filtro de aplicativos desatualizados",
"showOutdatedOnly": "Mostrar apenas aplicativos desatualizados",
"removeOutdatedFilter": "Remover filtro de aplicações desatualizadas",
"showOutdatedOnly": "Mostrar apenas aplicações desatualizadas",
"filter": "Filtro",
"filterApps": "Filtrar aplicativos",
"appName": "Nome do aplicativo",
"filterApps": "Filtrar aplicações",
"appName": "Nome da aplicação",
"author": "Autor",
"upToDateApps": "Aplicativos atualizados",
"nonInstalledApps": "Aplicativos não instalados",
"upToDateApps": "Aplicações atualizadas",
"nonInstalledApps": "Aplicações não instaladas",
"importExport": "Importar/Exportar",
"settings": "Configurações",
"exportedTo": "Exportado para {}",
@@ -97,14 +97,14 @@
"obtainiumImport": "Importar dados do Obtainium",
"importFromURLList": "Importar de lista de URLs",
"searchQuery": "Pesquisa",
"appURLList": "Lista de URLs de aplicativos",
"appURLList": "Lista de URLs de aplicações",
"line": "Linha",
"searchX": "Pesquisar na/o {}",
"noResults": "Nenhum resultado encontrado",
"importX": "Importar {}",
"importedAppsIdDisclaimer": "Aplicativos Importados podem ser mostrados incorretamente como \"Não Instalado\".\nPara consertar, reinstale-os usando o Obtainium.\nIsso não deve afetar dados do aplicativo.\n\nAfeta apenas métodos de importação de URL e de terceiros.",
"importedAppsIdDisclaimer": "Aplicações Importadas podem ser mostradas incorretamente como \"Não Instalado\".\nPara corrigir, reinstale-as usando o Obtainium.\nIsto não deve afetar os dados da aplicação.\n\nAfeta apenas os métodos de importação de URL e de terceiros.",
"importErrors": "Erros de importação",
"importedXOfYApps": "{} de {} aplicativos importados.",
"importedXOfYApps": "{} de {} aplicações importadas.",
"followingURLsHadErrors": "As seguintes URLs apresentaram erros:",
"selectURL": "Selecionar URL",
"selectURLs": "Selecionar URLs",
@@ -115,53 +115,53 @@
"followSystem": "Padrão do sistema",
"followSystemThemeExplanation": "O tema do sistema seguinte só é possível através da utilização de aplicações de terceiros",
"useBlackTheme": "Usar tema preto AMOLED",
"appSortBy": "Classificar aplicativo por",
"appSortBy": "Classificar aplicação por",
"authorName": "Autor/Nome",
"nameAuthor": "Nome/Autor",
"asAdded": "Como adicionado",
"appSortOrder": "Ordem de classificação de aplicativos",
"appSortOrder": "Ordem de classificação das aplicações",
"ascending": "Ascendente",
"descending": "Descendente",
"bgUpdateCheckInterval": "Intervalo de verificação de atualizações em segundo-plano",
"neverManualOnly": "Nunca - apenas manual",
"appearance": "Aparência",
"showWebInAppView": "Mostrar página web do aplicativo em informações do aplicativo",
"pinUpdates": "Fixar atualizações no topo da janela de aplicativos",
"showWebInAppView": "Mostrar a página web da aplicação nas informações da aplicação",
"pinUpdates": "Fixar atualizações no topo da janela das aplicações",
"updates": "Atualizações",
"sourceSpecific": "Token de acesso",
"appSource": "Fonte do aplicativo",
"appSource": "Fonte da aplicação",
"noLogs": "Sem logs",
"appLogs": "Logs do aplicativo",
"appLogs": "Logs da aplicação",
"close": "Fechar",
"share": "Compartilhar",
"appNotFound": "Aplicativo não encontrado",
"appNotFound": "Aplicação não encontrada",
"obtainiumExportHyphenatedLowercase": "exportação de obtainium",
"pickAnAPK": "Selecionar um APK",
"appHasMoreThanOnePackage": "{} tem mais de um pacote:",
"deviceSupportsXArch": "Seu dispositivo suporta a arquitetura de CPU {}.",
"deviceSupportsFollowingArchs": "Seu dispositivo suporta as seguintes arquiteturas de CPU:",
"warning": "Aviso",
"sourceIsXButPackageFromYPrompt": "A fonte do aplicativo é '{}' mas a origem do pacote é '{}'. Continuar?",
"sourceIsXButPackageFromYPrompt": "A fonte da aplicação é '{}' mas a origem do pacote é '{}'. Continuar?",
"updatesAvailable": "Atualizações disponíveis",
"updatesAvailableNotifDescription": "Notifica o usuário quando atualizações de um ou mais aplicativos monitorados pelo Obtainium estão disponíveis",
"updatesAvailableNotifDescription": "Notifica o utilizador quando as atualizações de uma ou mais aplicações monitorizadas pelo Obtainium estão disponíveis",
"noNewUpdates": "Sem novas atualizações.",
"xHasAnUpdate": "{} tem uma atualização.",
"appsUpdated": "Aplicativos atualizados",
"appsUpdated": "Aplicações atualizadas",
"appsNotUpdated": "Falha na atualização das aplicações",
"appsUpdatedNotifDescription": "Notifica o usuário quando atualizações foram aplicadas em segundo-plano para um ou mais aplicativos ",
"appsUpdatedNotifDescription": "Notifica o utilizador quando as atualizações foram aplicadas em segundo plano para uma ou mais aplicações ",
"xWasUpdatedToY": "{} foi atualizado para {}.",
"xWasNotUpdatedToY": "Falha ao atualizar {} para {}.",
"errorCheckingUpdates": "Erro ao procurar por atualizações",
"errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a checagem por atualizações em segundo-plano falha",
"appsRemoved": "Aplicativos removidos",
"appsRemovedNotifDescription": "Notifica o usuário quando um ou mais aplicativos foram removidos devido a erros de carregamento",
"appsRemoved": "Aplicações removidas",
"appsRemovedNotifDescription": "Notifica o utilizador quando uma ou mais aplicações foram removidas devido a erros de carregamento",
"xWasRemovedDueToErrorY": "{} foi removido devido a este erro: {}",
"completeAppInstallation": "Instalação do aplicativo concluída",
"obtainiumMustBeOpenToInstallApps": "Obtainium deve estar aberto para instalar os aplicativos",
"completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de um aplicativo",
"completeAppInstallation": "Instalação da aplicação concluída",
"obtainiumMustBeOpenToInstallApps": "O Obtainium deve estar aberto para instalar as aplicações",
"completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de uma aplicação",
"checkingForUpdates": "Verificando atualizações",
"checkingForUpdatesNotifDescription": "Notificação transiente que aparece quando o Obtainium está verificando se há atualizações",
"pleaseAllowInstallPerm": "Por favor, permita que o Obtainium possa instalar aplicativos",
"pleaseAllowInstallPerm": "Por favor, permita que o Obtainium instale aplicações",
"trackOnly": "Apenas monitorar",
"errorWithHttpStatusCode": "Erro {}",
"versionCorrectionDisabled": "Correção de versão desativada (plugin parece não funcionar)",
@@ -175,10 +175,10 @@
"remove": "Remover",
"yesMarkUpdated": "Sim, marcar como atualizado",
"fdroid": "Oficial F-Droid",
"appIdOrName": "ID do aplicativo ou nome",
"appId": "ID do aplicativo",
"appWithIdOrNameNotFound": "Nenhum aplicativo foi encontrado com esse ID ou nome",
"reposHaveMultipleApps": "Repositórios podem conter múltiplos aplicativos",
"appIdOrName": "ID da aplicação ou nome",
"appId": "ID da aplicação",
"appWithIdOrNameNotFound": "Nenhuma aplicação foi encontrada com esse ID ou nome",
"reposHaveMultipleApps": "Os repositórios podem conter múltiplas aplicações",
"fdroidThirdPartyRepo": "Repositórios de terceiros F-Droid",
"install": "Instalar",
"markInstalled": "Marcar instalado",
@@ -186,12 +186,12 @@
"markUpdated": "Marcar como atualizado",
"additionalOptions": "Opções adicionais",
"disableVersionDetection": "Desativar detecção de versão",
"noVersionDetectionExplanation": "Essa opção deve apenas ser usada por aplicativos onde a detecção de versão não funciona corretamente.",
"noVersionDetectionExplanation": "Esta opção deve ser usada apenas por aplicações onde a deteção de versão não funciona corretamente.",
"downloadingX": "Baixando {}",
"downloadX": "Descarregar {}",
"downloadedX": "Descarregado {}",
"releaseAsset": "Libertação de activos",
"downloadNotifDescription": "Notifica o usuário o progresso do download de um aplicativo",
"downloadNotifDescription": "Notifica o utilizador sobre o progresso do download de uma aplicação",
"noAPKFound": "APK não encontrado",
"noVersionDetection": "Sem detecção de versão",
"categorize": "Categorizar",
@@ -200,20 +200,20 @@
"noCategory": "Sem categoria",
"noCategories": "Sem categorias",
"deleteCategoriesQuestion": "Deletar categorias?",
"categoryDeleteWarning": "Todos os aplicativos em categorias removidas serão descategorizados.",
"categoryDeleteWarning": "Todas as aplicações em categorias removidas serão descategorizadas.",
"addCategory": "Adicionar categoria",
"label": "Etiqueta",
"language": "Linguagem",
"copiedToClipboard": "Copiado para a área de transferência",
"storagePermissionDenied": "Permissão de armazenamento negada",
"selectedCategorizeWarning": "Isso vai substituir qualquer configuração de categoria para os aplicativos selecionados.",
"selectedCategorizeWarning": "Isto irá substituir qualquer configuração de categoria para as aplicações selecionadas.",
"filterAPKsByRegEx": "Filtrar APKs usando expressão regular",
"removeFromObtainium": "Remover do Obtainium",
"uninstallFromDevice": "Desinstalar do dispositivo",
"onlyWorksWithNonVersionDetectApps": "Apenas funciona para aplicativos com detecção de versão desativada.",
"onlyWorksWithNonVersionDetectApps": "Apenas funciona para aplicações com a deteção de versão desativada.",
"releaseDateAsVersion": "Usar data de lançamento como versão",
"releaseTitleAsVersion": "Utilizar o título da versão como cadeia de versões",
"releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicativos onde a detecção de versão não funciona corretamente, mas uma data de lançamento disponível.",
"releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicações onde a deteção de versão não funciona corretamente, mas existe uma data de lançamento disponível.",
"changes": "Alterações",
"releaseDate": "Data de lançamento",
"importFromURLsInFile": "Importar de URLs em arquivo (formato OPML)",
@@ -227,15 +227,15 @@
"dontShowAgain": "Não mostrar isso novamente",
"dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas monitorar'",
"dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK",
"moveNonInstalledAppsToBottom": "Mover aplicativos não instalados para o fundo da lista de aplicativos",
"moveNonInstalledAppsToBottom": "Mover aplicações não instaladas para o fundo da lista de aplicações",
"gitlabPATLabel": "Token de acesso pessoal do Gitlab",
"about": "Sobre",
"requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)",
"checkOnStart": "Verificar se há atualizações ao iniciar",
"tryInferAppIdFromCode": "Tente inferir o ID do aplicativo pelo código-fonte",
"removeOnExternalUninstall": "Remover automaticamente aplicativos desinstalados externamente",
"tryInferAppIdFromCode": "Tentar inferir o ID da aplicação a partir do código-fonte",
"removeOnExternalUninstall": "Remover automaticamente aplicações desinstaladas externamente",
"pickHighestVersionCode": "Auto-selecionar o maior número de versão do APK",
"checkUpdateOnDetailPage": "Checar por atualizações ao abrir a página de detalhes de um aplicativo",
"checkUpdateOnDetailPage": "Checar por atualizações ao abrir a página de detalhes de uma aplicação",
"disablePageTransitions": "Desativar animações de transição de página",
"reversePageTransitions": "Animações de transição de página invertidas",
"minStarCount": "Contagem mínima de estrelas",
@@ -245,11 +245,11 @@
"sortByLastLinkSegment": "Ordenar apenas usando o último segmento do link",
"filterReleaseNotesByRegEx": "Filtrar notas de versão usando Regex",
"customLinkFilterRegex": "Filtro de link personalizado usando expressão regular (Padrão '.apk$')",
"appsPossiblyUpdated": "Tentativas de atualização de aplicativos",
"appsPossiblyUpdatedNotifDescription": "Notifica o usuário de que atualizações de um ou mais aplicativos foram potencialmente aplicadas em segundo-plano",
"appsPossiblyUpdated": "Aplicações possivelmente atualizadas",
"appsPossiblyUpdatedNotifDescription": "Notifica o utilizador de que as atualizações de uma ou mais aplicações foram potencialmente aplicadas em segundo plano",
"xWasPossiblyUpdatedToY": "{} pode ter sido atualizado para {}.",
"enableBackgroundUpdates": "Ativar atualizações em segundo-plano",
"backgroundUpdateReqsExplanation": "Atualizações em segundo-plano podem não ser possíveis para todos os aplicativos.",
"backgroundUpdateReqsExplanation": "As atualizações em segundo plano podem não ser possíveis para todas as aplicações.",
"backgroundUpdateLimitsExplanation": "O sucesso de uma instalação em segundo-plano só pode ser determinado quando o Obtainium é aberto.",
"verifyLatestTag": "Verifique a 'última' etiqueta",
"intermediateLinkRegex": "Filtrar um link 'intermediário' para visitar",
@@ -282,14 +282,14 @@
"installing": "Instalando",
"skipUpdateNotifications": "Pular notificações de update",
"updatesAvailableNotifChannel": "Atualizações disponíveis",
"appsUpdatedNotifChannel": "Aplicativos atualizados",
"appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de aplicativos",
"appsUpdatedNotifChannel": "Aplicações atualizadas",
"appsPossiblyUpdatedNotifChannel": "Aplicações possivelmente atualizadas",
"errorCheckingUpdatesNotifChannel": "Erro ao procurar por atualizações",
"appsRemovedNotifChannel": "Aplicativos removidos",
"appsRemovedNotifChannel": "Aplicações removidas",
"downloadingXNotifChannel": "Baixando {}",
"completeAppInstallationNotifChannel": "Instalação completa do aplicativo",
"completeAppInstallationNotifChannel": "Instalação da aplicação concluída",
"checkingForUpdatesNotifChannel": "Checando por atualizações",
"onlyCheckInstalledOrTrackOnlyApps": "Apenas verificar atualizações de aplicativos instalados e 'Apenas monitorar'",
"onlyCheckInstalledOrTrackOnlyApps": "Apenas verificar atualizações de aplicações instaladas e 'Apenas monitorizar'",
"supportFixedAPKURL": "Suporte a APK com URLs fixas",
"selectX": "Selecionar {}",
"parallelDownloads": "Permitir downloads paralelos",
@@ -299,7 +299,7 @@
"shizukuOldAndroidWithADB": "Shizuku a funcionar no Android < 8.1 com ADB - atualizar o Android ou utilizar o Sui",
"shizukuPretendToBeGooglePlay": "Definir o Google Play como fonte de instalação (se for utilizado o Shizuku)",
"useSystemFont": "Usar fonte padrão do sistema",
"useVersionCodeAsOSVersion": "Usar versionCode do aplicativo como versão detectada pelo sistema operacional",
"useVersionCodeAsOSVersion": "Usar o versionCode da aplicação como a versão detetada pelo sistema operativo",
"requestHeader": "Requisitar cabeçalho",
"useLatestAssetDateAsReleaseDate": "Use o último upload de recursos como data de lançamento",
"defaultPseudoVersioningMethod": "Método de pseudo-versionamento padrão",
@@ -336,25 +336,28 @@
"foregroundServiceExplanation": "Utilizar um serviço em primeiro plano para verificação de actualizações (mais fiável, consome mais energia)",
"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 aplicativo?",
"other": "Remover aplicativos?"
"one": "Remover aplicação?",
"other": "Remover aplicações?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Muitas solicitações (taxa de solicitações limitada) - tente novamente em {} minuto",
"other": "Muitas solicitações (taxa limitada) - tente novamente em {} minutos"
"one": "Muitos pedidos (taxa de pedidos limitada) - tente novamente em {} minuto",
"other": "Muitos pedidos (taxa limitada) - tente novamente em {} minutos"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "A verificação de atualizações em segundo-plano encontrou um {}, agendada uma nova verificação em {} minuto",
"other": "A verificação de atualizações em segundo-plano encontrou um {}, agendada uma nova verificação em {} minutos"
"one": "A verificação de atualizações em segundo plano encontrou um {}, agendada uma nova verificação em {} minuto",
"other": "A verificação de atualizações em segundo plano encontrou um {}, agendada uma nova verificação em {} minutos"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "A verificação de atualizações em segundo-plano encontrou {} atualização, o usuário sera notificado caso necessário",
"other": "A verificação de atualizações em segundo-plano encontrou {} atualizações, o usuário sera notificado caso necessário"
"one": "A verificação de atualizações em segundo plano encontrou {} atualização, o utilizador será notificado caso necessário",
"other": "A verificação de atualizações em segundo plano encontrou {} atualizações, o utilizador será notificado caso necessário"
},
"apps": {
"one": "{} Aplicativo",
"other": "{} Aplicativos"
"one": "{} Aplicação",
"other": "{} Aplicações"
},
"url": {
"one": "{} URL",
@@ -377,20 +380,20 @@
"other": "Foram limpos {n} logs (antes = {antes}, depois = {depois})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} e um outro aplicativo possui atualizações.",
"other": "{} e {} outros aplicativo possuem atualizações."
"one": "{} e uma outra aplicação possui atualizações.",
"other": "{} e {} outras aplicações possuem atualizações."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} e um outro aplicativo foram atualizado.",
"other": "{} e {} outros aplicativos foram atualizados."
"one": "{} e uma outra aplicação foram atualizadas.",
"other": "{} e {} outras aplicações foram atualizadas."
},
"xAndNMoreUpdatesFailed": {
"one": "Falha ao atualizar {} e mais 1 aplicação.",
"other": "Falha ao atualizar {} e {} mais aplicações."
"other": "Falha ao atualizar {} e {} outras aplicações."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} e um outro aplicativo podem ter sido atualizados.",
"other": "{} e {} outros aplicativos podem ter sido atualizados."
"one": "{} e uma outra aplicação podem ter sido atualizadas.",
"other": "{} e {} outras aplicações podem ter sido atualizadas."
},
"apk": {
"one": "{} APK",

View File

@@ -253,8 +253,8 @@
"backgroundUpdateLimitsExplanation": "Успешность фоновой установки можно определить только после открытия Obtainium",
"verifyLatestTag": "Проверять метку «latest»",
"intermediateLinkRegex": "Фильтр для «промежуточной» ссылки для посещения",
"filterByLinkText": "Фильтрация ссылок по тексту ссылки",
"matchLinksOutsideATags": "Сопоставляйте ссылки вне тегов <a>",
"filterByLinkText": "Фильтровать ссылки по тексту ссылки",
"matchLinksOutsideATags": "Сопоставлять ссылки вне тегов <a>",
"intermediateLinkNotFound": "Промежуточная ссылка не найдена",
"intermediateLink": "Промежуточная ссылка",
"exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)",
@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Использовать приоритетную службу для проверки обновлений (надёжнее, энергозатратнее)",
"fgServiceNotice": "Это уведомление необходимо для фоновой проверки обновлений (оно может быть скрыто в настройках ОС)",
"excludeSecrets": "Исключить секреты",
"GHReqPrefix": "Экземпляр 'sky22333/hubproxy' для запросов на GitHub",
"includeZips": "Включить ZIP-файлы",
"zippedApkFilterRegEx": "Фильтр APK внутри ZIP",
"removeAppQuestion": {
"one": "Удалить приложение?",
"other": "Удалить приложения?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Använd en förgrundstjänst för uppdateringskontroll (mer tillförlitlig, förbrukar mer ström)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Güncelleme denetimi için bir ön plan hizmeti kullanın (daha güvenilir, daha fazla güç tüketir)",
"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?"

View File

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

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Sử dụng dịch vụ nền trước để kiểm tra cập nhật (đáng tin cậy hơn, tiêu tốn nhiều pin hơn)",
"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?"

View File

@@ -336,6 +336,9 @@
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
"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": "移除應用程式?"

View File

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

View File

@@ -82,13 +82,13 @@ class APKCombo extends AppSource {
}
@override
Future<String> apkUrlPrefetchModifier(
String apkUrl,
Future<String> assetUrlPrefetchModifier(
String assetUrl,
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
var freshURLs = await getApkUrls(standardUrl, additionalSettings);
var path2Match = Uri.parse(apkUrl).path;
var path2Match = Uri.parse(assetUrl).path;
for (var url in freshURLs) {
if (Uri.parse(url.value).path == path2Match) {
return url.value;

View File

@@ -1,7 +1,9 @@
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
@@ -9,6 +11,17 @@ class Farsroid extends AppSource {
Farsroid() {
hosts = ['farsroid.com'];
name = 'Farsroid';
naiveStandardVersionDetection = true;
additionalSourceAppSpecificSettingFormItems = [
[
GeneratedFormSwitch(
'useFirstApkOfVersion',
label: tr('useFirstApkOfVersion'),
defaultValue: true,
),
],
];
}
@override
@@ -57,15 +70,21 @@ class Farsroid extends AppSource {
if (html2.isEmpty) {
throw NoAPKError();
}
var apkLinks =
(await grabLinksCommon(html2, res2.request!.url, additionalSettings))
.map((l) => MapEntry(Uri.parse(l.key).pathSegments.last, l.key))
.where(
(l) => l.key.toLowerCase().startsWith(
'$appName-$version'.toLowerCase(),
),
)
.toList();
var apkLinks = (await grabLinksCommon(
html2,
res2.request!.url,
additionalSettings,
)).map((l) => MapEntry(Uri.parse(l.key).pathSegments.last, l.key)).toList();
if (additionalSettings['useFirstApkOfVersion'] == true) {
apkLinks = apkLinks
.where(
(l) => l.key.toLowerCase().startsWith(
'$appName-$version'.toLowerCase(),
),
)
.toList();
}
if (apkLinks.isEmpty) {
throw NoAPKError();

View File

@@ -32,6 +32,7 @@ class FDroid extends AppSource {
GeneratedFormSwitch(
'trySelectingSuggestedVersionCode',
label: tr('trySelectingSuggestedVersionCode'),
defaultValue: true,
),
],
[

View File

@@ -29,6 +29,13 @@ class FDroidRepo extends AppSource {
defaultValue: false,
),
],
[
GeneratedFormSwitch(
'trySelectingSuggestedVersionCode',
label: tr('trySelectingSuggestedVersionCode'),
defaultValue: true,
),
],
];
}
@@ -170,6 +177,7 @@ class FDroidRepo extends AppSource {
}
standardUrl = removeQueryParamsFromUrl(standardUrl);
bool pickHighestVersionCode = additionalSettings['pickHighestVersionCode'];
bool trySelectingSuggestedVersionCode = additionalSettings['trySelectingSuggestedVersionCode'];
if (appIdOrName == null) {
throw NoReleasesError();
}
@@ -207,38 +215,58 @@ class FDroidRepo extends AppSource {
foundApps[0].querySelector('name')?.innerHtml ?? appId;
var appName = foundApps[0].querySelector('name')?.innerHtml ?? appId;
var releases = foundApps[0].querySelectorAll('package');
if (releases.isEmpty) {
throw NoReleasesError();
}
String? changeLog = foundApps[0].querySelector('changelog')?.innerHtml;
String? latestVersion = releases[0].querySelector('version')?.innerHtml;
String? added = releases[0].querySelector('added')?.innerHtml;
DateTime? releaseDate = added != null ? DateTime.parse(added) : null;
if (latestVersion == null) {
throw NoVersionError();
}
var latestVersionReleases = releases
.where(
(element) =>
element.querySelector('version')?.innerHtml == latestVersion &&
element.querySelector('apkname') != null,
)
.toList();
if (latestVersionReleases.length > 1 && pickHighestVersionCode) {
latestVersionReleases.sort((e1, e2) {
return int.parse(
e2.querySelector('versioncode')!.innerHtml,
).compareTo(int.parse(e1.querySelector('versioncode')!.innerHtml));
});
latestVersionReleases = [latestVersionReleases[0]];
String? marketvercodeStr = foundApps[0].querySelector('marketvercode')?.innerHtml;
int? marketvercode = int.tryParse(marketvercodeStr ?? '');
List selectedReleases = [];
if (trySelectingSuggestedVersionCode && marketvercode != null) {
selectedReleases = releases.where((e) =>
int.tryParse(e.querySelector('versioncode')?.innerHtml ?? '') == marketvercode &&
e.querySelector('apkname') != null
).toList();
}
List<String> apkUrls = latestVersionReleases
String? appAuthorName = foundApps[0].querySelector('author')?.innerHtml;
if (appAuthorName != null) {
authorName = appAuthorName;
}
if (selectedReleases.isEmpty) {
selectedReleases = releases.where((e) =>
e.querySelector('version')?.innerHtml == latestVersion &&
e.querySelector('apkname') != null
).toList();
if (selectedReleases.length > 1 && pickHighestVersionCode) {
selectedReleases.sort((e1, e2) {
return int.parse(e2.querySelector('versioncode')!.innerHtml)
.compareTo(int.parse(e1.querySelector('versioncode')!.innerHtml));
});
selectedReleases = [selectedReleases[0]];
}
}
String? selectedVersion = selectedReleases[0].querySelector('version')?.innerHtml;
if (selectedVersion == null) {
throw NoVersionError();
}
String? added = selectedReleases[0].querySelector('added')?.innerHtml;
DateTime? releaseDate = added != null ? DateTime.parse(added) : null;
List<String> apkUrls = selectedReleases
.map(
(e) =>
'${res.request!.url.toString().split('/').reversed.toList().sublist(1).reversed.join('/')}/${e.querySelector('apkname')!.innerHtml}',
)
.toList();
return APKDetails(
latestVersion,
selectedVersion,
getApkUrlsFromUrls(apkUrls),
AppNames(authorName, appName),
releaseDate: releaseDate,
changeLog: changeLog,
);
} else {
throw getObtainiumHttpError(res);

View File

@@ -18,6 +18,7 @@ class GitHub extends AppSource {
appIdInferIsOptional = true;
showReleaseDateAsVersionToggle = true;
this.hostChanged = hostChanged;
allowIncludeZips = true;
sourceConfigSettingFormItems = [
GeneratedFormTextField(
@@ -45,6 +46,46 @@ class GitHub extends AppSource {
const SizedBox(height: 4),
],
),
GeneratedFormTextField(
'GHReqPrefix',
label: tr('GHReqPrefix'),
hint: 'gh-proxy.com',
required: false,
additionalValidators: [
(value) {
try {
if (value != null && Uri.parse(value).scheme.isNotEmpty) {
throw true;
}
if (value != null) {
Uri.parse('https://${value}/api.github.com');
}
} catch (e) {
return tr('invalidInput');
}
return null;
},
],
belowWidgets: [
const SizedBox(height: 4),
GestureDetector(
onTap: () {
launchUrlString(
'https://github.com/sky22333/hubproxy',
mode: LaunchMode.externalApplication,
);
},
child: Text(
tr('about'),
style: const TextStyle(
decoration: TextDecoration.underline,
fontSize: 12,
),
),
),
const SizedBox(height: 4),
],
),
];
additionalSourceAppSpecificSettingFormItems = [
@@ -249,6 +290,9 @@ class GitHub extends AppSource {
settingsProvider,
);
String? creds = sourceConfig['github-creds'];
if ((additionalSettings['GHReqPrefix'] as String? ?? '').isNotEmpty) {
creds = null;
}
if (creds != null) {
var userNameEndIndex = creds.indexOf(':');
if (userNameEndIndex > 0) {
@@ -270,6 +314,18 @@ class GitHub extends AppSource {
return null;
}
@override
Future<String> generalReqPrefetchModifier(
String reqUrl,
Map<String, dynamic> additionalSettings,
) async {
if ((additionalSettings['GHReqPrefix'] as String? ?? '').isNotEmpty) {
var uri = Uri.parse(reqUrl);
return 'https://${additionalSettings['GHReqPrefix']}/${uri.toString().substring('https://'.length)}';
}
return reqUrl;
}
Future<String> getAPIHost(Map<String, dynamic> additionalSettings) async =>
'https://api.${hosts[0]}';
@@ -289,6 +345,12 @@ class GitHub extends AppSource {
Map<String, dynamic> additionalSettings, {
Function(Response)? onHttpErrorCode,
}) async {
SettingsProvider settingsProvider = SettingsProvider();
await settingsProvider.initializeSettings();
var sourceConfigSettingValues = await getSourceConfigValues(
additionalSettings,
settingsProvider,
);
bool includePrereleases = additionalSettings['includePrereleases'] == true;
bool fallbackToOlderReleases =
additionalSettings['fallbackToOlderReleases'] == true;
@@ -309,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('?');
@@ -341,9 +404,11 @@ class GitHub extends AppSource {
findReleaseAssetUrls(dynamic release) =>
(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['url'] ?? e['browser_download_url']);
url = undoGHProxyMod(url, sourceConfigSettingValues);
e['final_url'] = (e['name'] != null) && (url != null)
? MapEntry(e['name'] as String, url as String)
: const MapEntry('', '');
@@ -480,14 +545,13 @@ class GitHub extends AppSource {
List<MapEntry<String, String>> allAssetUrls = allAssetsWithUrls
.map((e) => e['final_url'] as MapEntry<String, String>)
.toList();
var apkAssetsWithUrls = allAssetsWithUrls
.where(
(element) => (element['final_url'] as MapEntry<String, String>)
.key
.toLowerCase()
.endsWith('.apk'),
)
.toList();
var apkAssetsWithUrls = allAssetsWithUrls.where((element) {
var ext = (element['final_url'] as MapEntry<String, String>).key
.toLowerCase()
.split('.')
.last;
return ext == 'apk' || (includeZips && ext == 'zip');
}).toList();
var filteredApkUrls = filterApks(
apkAssetsWithUrls
@@ -522,7 +586,10 @@ class GitHub extends AppSource {
allAssetUrls.add(
MapEntry(
(targetRelease['version'] ?? 'source') + '.tar.gz',
targetRelease['tarball_url'],
undoGHProxyMod(
targetRelease['tarball_url'],
sourceConfigSettingValues,
),
),
);
}
@@ -530,7 +597,10 @@ class GitHub extends AppSource {
allAssetUrls.add(
MapEntry(
(targetRelease['version'] ?? 'source') + '.zip',
targetRelease['zipball_url'],
undoGHProxyMod(
targetRelease['zipball_url'],
sourceConfigSettingValues,
),
),
);
}
@@ -652,12 +722,23 @@ class GitHub extends AppSource {
}
}
undoGHProxyMod(
String reqUrl,
Map<String, String> sourceConfigSettingValues,
) => reqUrl.replaceFirst(
'https://${sourceConfigSettingValues['GHReqPrefix']}/',
'',
);
@override
Future<Map<String, List<String>>> search(
String query, {
Map<String, dynamic> querySettings = const {},
}) async {
return searchCommon(
var sp = SettingsProvider();
await sp.initializeSettings();
var sourceConfigSettingValues = await getSourceConfigValues({}, sp);
var results = await searchCommon(
query,
'${await getAPIHost({})}/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100',
'items',
@@ -666,6 +747,15 @@ class GitHub extends AppSource {
},
querySettings: querySettings,
);
if ((sourceConfigSettingValues['GHReqPrefix'] ?? '').isNotEmpty) {
Map<String, List<String>> results2 = {};
results.forEach((k, v) {
results2[undoGHProxyMod(k, sourceConfigSettingValues)] = v;
});
return results2;
} else {
return results;
}
}
void rateLimitErrorCheck(Response res) {

View File

@@ -129,14 +129,14 @@ class GitLab extends AppSource {
}
@override
Future<String> apkUrlPrefetchModifier(
String apkUrl,
Future<String> assetUrlPrefetchModifier(
String assetUrl,
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
String optionalAuth = (PAT != null) ? 'private_token=$PAT' : '';
return '$apkUrl${(Uri.parse(apkUrl).query.isEmpty ? '?' : '&')}$optionalAuth';
return '$assetUrl${(Uri.parse(assetUrl).query.isEmpty ? '?' : '&')}$optionalAuth';
}
@override

View File

@@ -146,10 +146,7 @@ Future<List<MapEntry<String, String>>> grabLinksCommon(
.map((e) => MapEntry(ensureAbsoluteUrl(e.key, reqUrl), e.value))
.toList();
if (allLinks.isEmpty || matchLinksOutsideATags) {
allLinks = getLinksInLines(rawBody);
}
if (allLinks.isEmpty) {
// Getting desperate
// Decode the body if the response is a JSON
try {
var jsonStrings = collectAllStringsFromJSONObject(jsonDecode(rawBody));
allLinks = getLinksInLines(jsonStrings.join('\n'));
@@ -163,7 +160,7 @@ Future<List<MapEntry<String, String>>> grabLinksCommon(
);
}
} catch (e) {
//
allLinks = getLinksInLines(rawBody);
}
}
List<MapEntry<String, String>> links = [];

View File

@@ -80,20 +80,20 @@ class RuStore extends AppSource {
}
Response res1 = await sourceRequest(
'https://backapi.rustore.ru/applicationData/download-link',
'https://backapi.rustore.ru/applicationData/v2/download-link',
additionalSettings,
followRedirects: false,
postBody: {"appId": appDetails['appId'], "firstInstall": true},
);
var downloadDetails = (await decodeJsonBody(res1.bodyBytes))['body'];
if (res1.statusCode != 200 || downloadDetails['apkUrl'] == null) {
if (res1.statusCode != 200 || downloadDetails['downloadUrls'][0]['url'] == null) {
throw NoAPKError();
}
return APKDetails(
version,
getApkUrlsFromUrls([
(downloadDetails['apkUrl'] as String).replaceAll(
(downloadDetails['downloadUrls'][0]['url'] as String).replaceAll(
RegExp('\\.zip\$'),
'.apk',
),

View File

@@ -124,12 +124,12 @@ class Uptodown extends AppSource {
}
@override
Future<String> apkUrlPrefetchModifier(
String apkUrl,
Future<String> assetUrlPrefetchModifier(
String assetUrl,
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
var res = await sourceRequest(apkUrl, additionalSettings);
var res = await sourceRequest(assetUrl, additionalSettings);
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
}

View File

@@ -70,12 +70,14 @@ class VivoAppStore extends AppSource {
throw NoReleasesError();
}
Map<String, List<String>> results = {};
var resultsJson = json['data']['appSearchResponse']['value'];
for (var item in (resultsJson as List<dynamic>)) {
results['$appDetailUrl${item['id']}'] = [
item['title_zh'].toString(),
item['developer'].toString(),
];
var resultsJson = json['data']['appSearchResponse']?['value'];
if (resultsJson != null) {
for (var item in (resultsJson as List<dynamic>)) {
results['$appDetailUrl${item['id']}'] = [
item['title_zh'].toString(),
item['developer'].toString(),
];
}
}
return results;
}

View File

@@ -203,11 +203,11 @@ class AddAppPageState extends State<AddAppPage> {
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;
}

View File

@@ -16,9 +16,14 @@ import 'package:provider/provider.dart';
import 'package:markdown/markdown.dart' as md;
class AppPage extends StatefulWidget {
const AppPage({super.key, required this.appId});
const AppPage({
super.key,
required this.appId,
this.showOppositeOfPreferredView = false,
});
final String appId;
final bool showOppositeOfPreferredView;
@override
State<AppPage> createState() => _AppPageState();
@@ -60,6 +65,11 @@ class _AppPageState extends State<AppPage> {
Widget build(BuildContext context) {
var appsProvider = context.watch<AppsProvider>();
var settingsProvider = context.watch<SettingsProvider>();
var showAppWebpageFinal =
(settingsProvider.showAppWebpage &&
!widget.showOppositeOfPreferredView) ||
(!settingsProvider.showAppWebpage &&
widget.showOppositeOfPreferredView);
getUpdate(String id, {bool resetVersion = false}) async {
try {
setState(() {
@@ -565,7 +575,7 @@ class _AppPageState extends State<AppPage> {
icon: const Icon(Icons.settings),
tooltip: tr('settings'),
),
if (app != null && settingsProvider.showAppWebpage)
if (app != null && showAppWebpageFinal)
IconButton(
onPressed: () {
showDialog(
@@ -661,10 +671,10 @@ class _AppPageState extends State<AppPage> {
);
return Scaffold(
appBar: settingsProvider.showAppWebpage ? AppBar() : appScreenAppBar(),
appBar: showAppWebpageFinal ? AppBar() : appScreenAppBar(),
backgroundColor: Theme.of(context).colorScheme.surface,
body: RefreshIndicator(
child: settingsProvider.showAppWebpage
child: showAppWebpageFinal
? getAppWebView()
: CustomScrollView(
slivers: [

View File

@@ -451,40 +451,57 @@ class AppsPageState extends State<AppsPage> {
}
getAppIcon(int appIndex) {
return FutureBuilder(
future: appsProvider.updateAppIcon(listedApps[appIndex].app.id),
builder: (ctx, val) {
return listedApps[appIndex].icon != null
? Image.memory(
listedApps[appIndex].icon!,
gaplessPlayback: true,
opacity: AlwaysStoppedAnimation(
listedApps[appIndex].installedInfo == null ? 0.6 : 1,
),
)
: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(0.31),
child: Padding(
padding: const EdgeInsets.all(15),
child: Image(
image: const AssetImage(
'assets/graphics/icon_small.png',
return GestureDetector(
child: FutureBuilder(
future: appsProvider.updateAppIcon(listedApps[appIndex].app.id),
builder: (ctx, val) {
return listedApps[appIndex].icon != null
? Image.memory(
listedApps[appIndex].icon!,
gaplessPlayback: true,
opacity: AlwaysStoppedAnimation(
listedApps[appIndex].installedInfo == null ? 0.6 : 1,
),
)
: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(0.31),
child: Padding(
padding: const EdgeInsets.all(15),
child: Image(
image: const AssetImage(
'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,
),
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(0.4)
: Colors.white.withOpacity(0.3),
colorBlendMode: BlendMode.modulate,
gaplessPlayback: true,
),
),
),
],
);
],
);
},
),
onDoubleTap: () {
pm.openApp(listedApps[appIndex].app.id);
},
onLongPress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AppPage(
appId: listedApps[appIndex].app.id,
showOppositeOfPreferredView: true,
),
),
);
},
);
}
@@ -993,6 +1010,7 @@ class AppsPageState extends State<AppsPage> {
selectedApps.where((element) => element.pinned).isEmpty
? tr('pinToTop')
: tr('unpinFromTop'),
textAlign: TextAlign.center,
),
),
const Divider(),
@@ -1009,7 +1027,10 @@ class AppsPageState extends State<AppsPage> {
);
Navigator.of(context).pop();
},
child: Text(tr('shareSelectedAppURLs')),
child: Text(
tr('shareSelectedAppURLs'),
textAlign: TextAlign.center,
),
),
const Divider(),
TextButton(
@@ -1026,7 +1047,10 @@ class AppsPageState extends State<AppsPage> {
subject: 'Obtainium - ${tr('appsString')}',
);
},
child: Text(tr('shareAppConfigLinks')),
child: Text(
tr('shareAppConfigLinks'),
textAlign: TextAlign.center,
),
),
const Divider(),
TextButton(
@@ -1052,7 +1076,10 @@ class AppsPageState extends State<AppsPage> {
fileNameOverrides: ['$fn.json'],
);
},
child: Text('${tr('share')} - ${tr('obtainiumExport')}'),
child: Text(
'${tr('share')} - ${tr('obtainiumExport')}',
textAlign: TextAlign.center,
),
),
const Divider(),
TextButton(
@@ -1076,6 +1103,7 @@ class AppsPageState extends State<AppsPage> {
'downloadX',
args: [lowerCaseIfEnglish(tr('releaseAsset'))],
),
textAlign: TextAlign.center,
),
),
const Divider(),
@@ -1083,7 +1111,10 @@ class AppsPageState extends State<AppsPage> {
onPressed: appsProvider.areDownloadsRunning()
? null
: showMassMarkDialog,
child: Text(tr('markSelectedAppsUpdated')),
child: Text(
tr('markSelectedAppsUpdated'),
textAlign: TextAlign.center,
),
),
],
),

View File

@@ -319,13 +319,24 @@ class _SettingsPageState extends State<SettingsPage> {
if (e.sourceConfigSettingFormItems.isNotEmpty) {
return GeneratedForm(
items: e.sourceConfigSettingFormItems.map((e) {
e.defaultValue = settingsProvider.getSettingString(e.key);
if (e is GeneratedFormSwitch) {
e.defaultValue = settingsProvider.getSettingBool(e.key);
} else {
e.defaultValue = settingsProvider.getSettingString(e.key);
}
return [e];
}).toList(),
onValueChanges: (values, valid, isBuilding) {
if (valid && !isBuilding) {
values.forEach((key, value) {
settingsProvider.setSettingString(key, value);
var formItem = e.sourceConfigSettingFormItems
.where((i) => i.key == key)
.firstOrNull;
if (formItem is GeneratedFormSwitch) {
settingsProvider.setSettingBool(key, value == true);
} else {
settingsProvider.setSettingString(key, value ?? '');
}
});
}
},

View File

@@ -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<String> generateStandardVersionRegExStrings() {
@@ -606,10 +609,20 @@ class AppsProvider with ChangeNotifier {
app.url,
overrideSource: app.overrideSource,
);
String downloadUrl = await source.apkUrlPrefetchModifier(
app.apkUrls[app.preferredApkIndex].value,
var additionalSettingsPlusSourceConfig = {
...app.additionalSettings,
...(await source.getSourceConfigValues(
app.additionalSettings,
settingsProvider,
)),
};
String downloadUrl = await source.assetUrlPrefetchModifier(
await source.generalReqPrefetchModifier(
app.apkUrls[app.preferredApkIndex].value,
additionalSettingsPlusSourceConfig,
),
app.url,
app.additionalSettings,
additionalSettingsPlusSourceConfig,
);
var notif = DownloadNotification(app.finalName, 100);
notificationsProvider?.cancel(notif.id);
@@ -654,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();
@@ -681,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(
@@ -718,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);
@@ -764,10 +799,13 @@ class AppsProvider with ChangeNotifier {
int? targetSDK = (await getInstalledInfo(
app.id,
))?.applicationInfo?.targetSdkVersion;
int requiredSDK = osInfo.version.sdkInt - 3;
// The APK should target a new enough API
// https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int)
if (!(targetSDK != null && targetSDK >= (osInfo.version.sdkInt - 3))) {
logs.add('Multiple APK URLs: ${app.id}');
if (!(targetSDK != null && targetSDK >= requiredSDK)) {
logs.add(
'App currently targets API ${targetSDK} which is too low for background updates (requires API ${requiredSDK}): ${app.id}',
);
return false;
}
@@ -813,15 +851,16 @@ class AppsProvider with ChangeNotifier {
);
}
Future<bool> installXApkDir(
DownloadedXApkDir dir,
Future<bool> 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();
@@ -850,7 +889,7 @@ class AppsProvider with ChangeNotifier {
}
try {
await installApk(
var wasInstalled = await installApk(
DownloadedApk(dir.appId, APKFiles[0]),
firstTimeWithContext,
needsBGWorkaround: needsBGWorkaround,
@@ -859,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) {
@@ -1135,7 +1174,7 @@ class AppsProvider with ChangeNotifier {
String id,
bool willBeSilent,
DownloadedApk? downloadedFile,
DownloadedXApkDir? downloadedDir,
DownloadedDir? downloadedDir,
) async {
apps[id]?.downloadProgress = -1;
notifyListeners();
@@ -1170,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,
@@ -1214,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
@@ -1227,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);
@@ -1279,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;
@@ -1310,7 +1349,8 @@ class AppsProvider with ChangeNotifier {
MapEntry<String, String>? 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);
}
@@ -1324,15 +1364,26 @@ class AppsProvider with ChangeNotifier {
evenIfSingleChoice: true,
);
if (tempFileUrl != null) {
var s = SourceProvider().getSource(
apps[id]!.app.url,
overrideSource: apps[id]!.app.overrideSource,
);
var additionalSettingsPlusSourceConfig = {
...apps[id]!.app.additionalSettings,
...(await s.getSourceConfigValues(
apps[id]!.app.additionalSettings,
settingsProvider,
)),
};
fileUrl = MapEntry(
tempFileUrl.key,
await (SourceProvider().getSource(
await s.assetUrlPrefetchModifier(
await s.generalReqPrefetchModifier(
tempFileUrl.value,
additionalSettingsPlusSourceConfig,
),
apps[id]!.app.url,
overrideSource: apps[id]!.app.overrideSource,
)).apkUrlPrefetchModifier(
tempFileUrl.value,
apps[id]!.app.url,
apps[id]!.app.additionalSettings,
additionalSettingsPlusSourceConfig,
),
);
}

View File

@@ -249,6 +249,15 @@ class SettingsProvider with ChangeNotifier {
notifyListeners();
}
bool? getSettingBool(String settingId) {
return prefs?.getBool(settingId) ?? false;
}
void setSettingBool(String settingId, bool value) {
prefs?.setBool(settingId, value);
notifyListeners();
}
Map<String, int> get categories =>
Map<String, int>.from(jsonDecode(prefs?.getString('categories') ?? '{}'));

View File

@@ -636,6 +636,7 @@ abstract class AppSource {
bool versionDetectionDisallowed = false;
List<String> excludeCommonSettingKeys = [];
bool urlsAlwaysHaveExtension = false;
bool allowIncludeZips = false;
AppSource() {
name = runtimeType.toString();
@@ -686,14 +687,27 @@ abstract class AppSource {
bool followRedirects = true,
Object? postBody,
}) async {
var sp = SettingsProvider();
await sp.initializeSettings();
getSourceConfigValues(additionalSettings, sp);
var additionalSettingsPlusSourceConfig = {
...additionalSettings,
...(await getSourceConfigValues(additionalSettings, sp)),
};
url = await generalReqPrefetchModifier(
url,
additionalSettingsPlusSourceConfig,
);
var method = postBody == null ? 'GET' : 'POST';
var requestHeaders = await getRequestHeaders(additionalSettings);
var requestHeaders = await getRequestHeaders(
additionalSettingsPlusSourceConfig,
);
var streamedResponseUrlWithResponseAndClient =
await sourceRequestStreamResponse(
method,
url,
requestHeaders,
additionalSettings,
additionalSettingsPlusSourceConfig,
followRedirects: followRedirects,
postBody: postBody,
);
@@ -821,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 {
if (showReleaseDateAsVersionToggle == true) {
if (additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
@@ -865,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',
@@ -880,6 +920,7 @@ abstract class AppSource {
return [
...additionalSourceAppSpecificSettingFormItems,
...additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly,
...moreConditionalItems,
];
}
@@ -911,12 +952,19 @@ abstract class AppSource {
return null;
}
Future<String> apkUrlPrefetchModifier(
String apkUrl,
Future<String> assetUrlPrefetchModifier(
String assetUrl,
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
return apkUrl;
return assetUrl;
}
Future<String> generalReqPrefetchModifier(
String reqUrl,
Map<String, dynamic> additionalSettings,
) async {
return reqUrl;
}
bool canSearch = false;

View File

@@ -5,10 +5,10 @@ packages:
dependency: "direct main"
description:
name: android_intent_plus
sha256: "2329378af63f49b985cb2e110ac784d08374f1e2b1984be77ba9325b1c8cce11"
sha256: "14a9f94c5825a528e8c38ee89a33dbeba947efbbf76f066c174f4f3ae4f48feb"
url: "https://pub.dev"
source: hosted
version: "5.3.1"
version: "6.0.0"
android_package_installer:
dependency: "direct main"
description:
@@ -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:
@@ -104,10 +104,10 @@ packages:
dependency: "direct main"
description:
name: battery_plus
sha256: "03d5a6bb36db9d2b977c548f6b0262d5a84c4d5a4cfee2edac4a91d57011b365"
sha256: ad16fcb55b7384be6b4bbc763d5e2031ac7ea62b2d9b6b661490c7b9741155bf
url: "https://pub.dev"
source: hosted
version: "6.2.3"
version: "7.0.0"
battery_plus_platform_interface:
dependency: transitive
description:
@@ -160,10 +160,10 @@ packages:
dependency: "direct main"
description:
name: connectivity_plus
sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec
sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c"
url: "https://pub.dev"
source: hosted
version: "6.1.5"
version: "7.0.0"
connectivity_plus_platform_interface:
dependency: transitive
description:
@@ -216,10 +216,10 @@ packages:
dependency: "direct main"
description:
name: device_info_plus
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
sha256: "49413c8ca514dea7633e8def233b25efdf83ec8522955cc2c0e3ad802927e7c6"
url: "https://pub.dev"
source: hosted
version: "11.5.0"
version: "12.1.0"
device_info_plus_platform_interface:
dependency: transitive
description:
@@ -232,10 +232,10 @@ packages:
dependency: "direct main"
description:
name: dynamic_color
sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d
sha256: "43a5a6679649a7731ab860334a5812f2067c2d9ce6452cf069c5e0c25336c17c"
url: "https://pub.dev"
source: hosted
version: "1.7.0"
version: "1.8.1"
easy_localization:
dependency: "direct main"
description:
@@ -288,10 +288,10 @@ packages:
dependency: "direct main"
description:
name: file_picker
sha256: e7e16c9d15c36330b94ca0e2ad8cb61f93cd5282d0158c09805aed13b5452f22
sha256: f2d9f173c2c14635cc0e9b14c143c49ef30b4934e8d1d274d6206fcb0086a06f
url: "https://pub.dev"
source: hosted
version: "10.3.2"
version: "10.3.3"
fixnum:
dependency: transitive
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
@@ -508,10 +508,10 @@ packages:
dependency: "direct main"
description:
name: fluttertoast
sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1"
sha256: "144ddd74d49c865eba47abe31cbc746c7b311c82d6c32e571fd73c4264b740e2"
url: "https://pub.dev"
source: hosted
version: "8.2.12"
version: "9.0.0"
fraction:
dependency: transitive
description:
@@ -572,10 +572,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0"
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.1"
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
@@ -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:
@@ -828,10 +828,10 @@ packages:
dependency: "direct main"
description:
name: share_plus
sha256: d7dc0630a923883c6328ca31b89aa682bacbf2f8304162d29f7c6aaff03a27a1
sha256: "3424e9d5c22fd7f7590254ba09465febd6f8827c8b19a44350de4ac31d92d3a6"
url: "https://pub.dev"
source: hosted
version: "11.1.0"
version: "12.0.0"
share_plus_platform_interface:
dependency: transitive
description:
@@ -852,10 +852,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: a2608114b1ffdcbc9c120eb71a0e207c71da56202852d4aab8a5e30a82269e74
sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713"
url: "https://pub.dev"
source: hosted
version: "2.4.12"
version: "2.4.15"
shared_preferences_foundation:
dependency: transitive
description:
@@ -1059,10 +1059,10 @@ packages:
dependency: transitive
description:
name: url_launcher_android
sha256: "69ee86740f2847b9a4ba6cffa74ed12ce500bbe2b07f3dc1e643439da60637b7"
sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9"
url: "https://pub.dev"
source: hosted
version: "6.3.18"
version: "6.3.24"
url_launcher_ios:
dependency: transitive
description:
@@ -1155,10 +1155,10 @@ packages:
dependency: transitive
description:
name: webview_flutter_android
sha256: "9a25f6b4313978ba1c2cda03a242eea17848174912cfb4d2d8ee84a556f248e3"
sha256: e5201c620eb2637dca88a756961fae4a7191bb30b4f2271e08b746405ffdf3fd
url: "https://pub.dev"
source: hosted
version: "4.10.1"
version: "4.10.5"
webview_flutter_platform_interface:
dependency: transitive
description:
@@ -1171,18 +1171,18 @@ packages:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: fb46db8216131a3e55bcf44040ca808423539bc6732e7ed34fb6d8044e3d512f
sha256: fea63576b3b7e02b2df8b78ba92b48ed66caec2bb041e9a0b1cbd586d5d80bfd
url: "https://pub.dev"
source: hosted
version: "3.23.0"
version: "3.23.1"
win32:
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:
@@ -1209,4 +1209,4 @@ packages:
version: "6.3.0"
sdks:
dart: ">=3.9.0 <4.0.0"
flutter: ">=3.29.0"
flutter: ">=3.35.0"

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
# 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.4+2320
version: 1.2.7+2323
environment:
sdk: ^3.8.1
@@ -45,8 +45,8 @@ dependencies:
shared_preferences: ^2.5.3
url_launcher: ^6.3.1
permission_handler: ^12.0.0+1
fluttertoast: ^8.2.12
device_info_plus: ^11.4.0
fluttertoast: ^9.0.0
device_info_plus: ^12.1.0
file_picker: ^10.1.9
animations: ^2.0.11
android_package_installer: # TODO: See if PR will be accepted (dev may not be active), else remove this comment
@@ -57,14 +57,14 @@ dependencies:
git:
url: https://github.com/ImranR98/android_package_manager
ref: master
share_plus: ^11.0.0
share_plus: ^12.0.0
sqflite: ^2.4.2
easy_localization: ^3.0.7+1
android_intent_plus: ^5.3.0
android_intent_plus: ^6.0.0
flutter_markdown: ^0.7.7+1
flutter_archive: ^6.0.3
hsluv: ^1.1.3
connectivity_plus: ^6.1.4
connectivity_plus: ^7.0.0
shared_storage: # TODO: Is this maintained?
git:
url: https://github.com/AlexBacich/shared-storage
@@ -85,7 +85,7 @@ dependencies:
ref: master
markdown: ^7.3.0
flutter_typeahead: ^5.2.0
battery_plus: ^6.2.1
battery_plus: ^7.0.0
flutter_charset_detector: ^5.0.0
# The "flutter_lints" package below contains a set of recommended lints to