Compare commits

...

55 Commits

Author SHA1 Message Date
Imran
b63a798d86 Merge pull request #1180 from ImranR98/dev
- Fix installed version bug when importing apps (#1179)
- Share button now shares Obtainium "protocol" links
2023-12-22 20:14:07 -06:00
Imran Remtulla
eacf3777a4 Update packages, increment version 2023-12-22 21:13:15 -05:00
Imran Remtulla
a5a7436bb1 Share button now shares Obtainium "protocol" links 2023-12-22 21:12:07 -05:00
Imran Remtulla
2a4cc35df7 Fix installed version bug when importing apps (#1179) 2023-12-22 21:10:11 -05:00
Imran Remtulla
cdccf58b76 Merge remote-tracking branch 'origin/dev' 2023-12-22 21:00:09 -05:00
Imran Remtulla
27300383a1 Add Wiki link 2023-12-22 20:59:49 -05:00
Imran
13066b3b4a Merge pull request #1177 from ImranR98/dev
- Add "Don't Sort" option to the HTML Source (#1168 )
- Fix syntax error in translation file (#1169)
- Parallel download option (#1175)
2023-12-21 15:02:58 -06:00
Imran Remtulla
ccbe9d00c8 Update packages, increment version 2023-12-21 16:01:42 -05:00
Imran Remtulla
ce291582cb Add "Don't Sort" option to the HTML Source (#1168 ) 2023-12-21 15:57:59 -05:00
Imran Remtulla
bb37bc3b51 Fix syntax error in translation file (#1169) 2023-12-21 15:46:38 -05:00
Imran Remtulla
5a7747acd1 Merge remote-tracking branch 'origin/main' into dev 2023-12-21 15:45:03 -05:00
Imran Remtulla
1bc2ec9461 Parallel download option (#1169) 2023-12-21 15:44:38 -05:00
Imran
2b977fc2b0 Merge pull request #1173 from drizzt/fix_1142
Resize adaptative icon to 108x108dp (#1142)
2023-12-21 14:24:21 -06:00
Timothy Redaelli
cc4b016c64 Resize adaptative icon to 108x108dp (#1142)
Android documentation [1] explicitly specify that adaptive icons must be
108x108dp.
Having bigger Adaptive icons triggers a bug in Android 14 QPR1 that
prevent to install apps from apk directly when the icons are bigger.

[1] https://developer.android.com/develop/ui/views/launch/icon_design_adaptive
2023-12-20 00:00:45 +01:00
Imran
f64f561d6f Merge pull request #1167 from ImranR98/dev
Update issue templates
2023-12-17 16:00:25 -06:00
Imran Remtulla
80bddf8a6b Update issue templates 2023-12-17 16:59:23 -05:00
Imran
cbaaec961c Merge pull request #1166 from ImranR98/dev
- Get real author name for F-Droid (#1076)
- Always add Track-only apps with random ID (#1138)
- Minor UI fix (#1162)
- Fix 'verify latest' option for GitHub (#1163)
2023-12-17 13:57:34 -06:00
Imran Remtulla
5477b3f936 Update packages, increment version, run dart fix 2023-12-17 14:54:54 -05:00
Imran Remtulla
fd59a93ede Get real author name for F-Droid (#1076) 2023-12-17 14:47:22 -05:00
Imran Remtulla
cd316b7138 Always add Track-only apps with random ID (#1138) 2023-12-17 14:36:06 -05:00
Imran Remtulla
d1955192ed Minor UI fix (#1162) 2023-12-17 14:14:46 -05:00
Imran Remtulla
9beb839bf4 Fix 'verify latest' option for GitHub (#1163) 2023-12-17 14:13:26 -05:00
Imran
29ea303093 Merge pull request #1161 from ImranR98/dev
- Custom link support (#368, #918)
- Export settings (#1157)
- Use public GitLab search API (#1147)
- Fix unauthorized error (#1153)
2023-12-16 03:12:10 -06:00
Imran Remtulla
feff6751ca Merge remote-tracking branch 'origin/main' into dev 2023-12-16 04:08:56 -05:00
Imran
ca33fdf752 Merge pull request #1133 from CertainBot/patch-1
Update Spanish translation
2023-12-16 03:07:03 -06:00
Imran
fdcdfe89d6 Merge pull request #1149 from gidano/main
Update hu.json
2023-12-16 03:06:53 -06:00
Imran
48ed2115a7 Merge pull request #1150 from Erudaro/main
Update bs.json
2023-12-16 03:06:44 -06:00
Imran Remtulla
65988f4e08 Link UI bugfix 2023-12-16 04:02:10 -05:00
Imran Remtulla
ede65eda6c bug 2023-12-16 03:47:29 -05:00
Imran Remtulla
5da56acac8 bug 2023-12-16 03:45:15 -05:00
Imran Remtulla
5720c55301 bugs 2023-12-16 03:43:55 -05:00
Imran Remtulla
ffefa4b30e Update packages, increment version 2023-12-16 02:57:08 -05:00
Imran Remtulla
80e4986b23 Export settings (#1157) 2023-12-16 02:55:05 -05:00
Imran Remtulla
dc92ccda0a Use public GitLab search API (#1147) 2023-12-16 02:01:43 -05:00
Imran Remtulla
f9bab18076 Fix unauthorized error (#1153) 2023-12-16 01:44:02 -05:00
Imran Remtulla
2dec52e221 Bugfix 2023-12-16 01:24:43 -05:00
Imran Remtulla
7413f693d7 JSON import link support (#368) 2023-12-16 00:31:32 -05:00
Imran Remtulla
415460df75 Custom link support (#918) 2023-12-15 23:37:04 -05:00
Erudaro
125a194468 Update bs.json 2023-12-10 22:07:14 +01:00
gidano
32e9afbf36 Update hu.json 2023-12-10 17:16:08 +01:00
Imran Remtulla
3eca704f4a Merge pull request #1137 from ImranR98/dev
- Add regex filter to all select dialogs (#1110)
- Let users store a custom note per app (#1126)
- Better GitLab error message (#1106)
2023-12-07 20:57:48 -06:00
Imran Remtulla
9c95129311 Better GitLab error message (#1106) 2023-12-07 21:55:35 -05:00
Imran Remtulla
bf34c1bcdb Let users store a custom note per app (#1126) 2023-12-07 21:47:56 -05:00
Imran Remtulla
284c687d77 Add regex filter to all select dialogs (#1110) 2023-12-07 21:38:16 -05:00
Imran Remtulla
09afb5a3f5 Upgrade packages, increment version 2023-12-07 21:00:51 -05:00
Imran Remtulla
0138721451 Merge pull request #1118 from DwainZwerg/patch-9
Update de.json
2023-12-07 19:56:13 -06:00
Imran Remtulla
2d5f610941 Merge branch 'main' into patch-9 2023-12-07 19:56:07 -06:00
Imran Remtulla
864fa7762b Merge pull request #1119 from iDazai/patch-2
Update de.json
2023-12-07 19:55:16 -06:00
Imran Remtulla
4fde38ee6a Merge pull request #1125 from Daviteusz/weblate-obtainium-translate
Update Polish translation
2023-12-07 19:55:05 -06:00
Imran Remtulla
6cdf0f10d4 Merge pull request #1136 from mehdeej/main
Update (Persian) fa.json
2023-12-07 19:54:56 -06:00
Mehdee
b66592c25f Update (Persian) fa.json 2023-12-07 20:58:26 +03:30
CertainBot
43616c566d Update es.json
- Spanish translation updated. Typos corrected.
- Traducción al castellano actualizada. Corrección de erratas.
2023-12-06 00:35:30 +01:00
Daviteusz
62f1dc17a0 Update Polish translations 2023-11-28 18:08:57 +01:00
iDazai
0e9a8a937a Update de.json
translated newly added strings
2023-11-25 14:06:50 +01:00
DwainZwerg
9a86b245ce Update de.json 2023-11-25 12:52:32 +00:00
41 changed files with 810 additions and 455 deletions

View File

@@ -9,6 +9,7 @@ assignees: ''
**Prerequisites**
<!-- Please ensure your request is not part of an existing issue. -->
<!-- Please ensure you have checked the Obtainium Wiki. -->
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->

View File

@@ -9,6 +9,7 @@ assignees: ''
**Prerequisites**
<!-- Please ensure your request is not part of an existing issue. -->
<!-- Please ensure you have checked the Obtainium Wiki. -->
**Describe the feature**
<!-- A clear and concise description of what you want to happen.

View File

@@ -6,6 +6,8 @@ Obtainium allows you to install and update Apps directly from their releases pag
Motivation: [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0)
Wiki: [https://github.com/ImranR98/Obtainium/wiki](https://github.com/ImranR98/Obtainium/wiki)
Currently supported App sources:
- Open Source - General:
- [GitHub](https://github.com/)

View File

@@ -28,8 +28,15 @@
<intent-filter>
<action
android:name="com.android_package_installer.content.SESSION_API_PACKAGE_INSTALLED"
android:exported="false"/>
android:exported="false" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="obtainium" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
@@ -39,10 +46,10 @@
<service
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"/>
android:exported="false" />
<receiver
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
android:exported="false"/>
android:exported="false" />
<receiver
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
android:enabled="false"
@@ -52,24 +59,24 @@
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="dev.imranr.obtainium"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
android:name="androidx.core.content.FileProvider"
android:authorities="dev.imranr.obtainium"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/>
android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
</manifest>

View File

@@ -1,8 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="142.129"
android:viewportHeight="142.129"
android:width="503.6066dp"
android:height="503.6066dp">
android:width="108dp"
android:height="108dp">
<group
android:translateX="-30.39437"
android:translateY="-54.68043">

View File

@@ -88,7 +88,7 @@
"importExport": "Uvoz/izvoz",
"settings": "Postavke",
"exportedTo": "Izvezeno u {}",
"obtainiumExport": "Obtainium Export",
"obtainiumExport": "Obtainium izvoz",
"invalidInput": "Neispravan unos.",
"importedX": "Uvezeno {}",
"obtainiumImport": "Obtainium uvoz",
@@ -134,7 +134,7 @@
"close": "Zatvori",
"share": "Podijeli",
"appNotFound": "Aplikacija nije pronađena",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"obtainiumExportHyphenatedLowercase": "obtainium-izvoz",
"pickAnAPK": "Odaberite APK",
"appHasMoreThanOnePackage": "{} ima više od jednog paketa:",
"deviceSupportsXArch": "Vaš uređaj podržava {} arhitekturu procesora.",
@@ -231,52 +231,56 @@
"checkUpdateOnDetailPage": "Provjerite ima li novosti pri otvaranju stranice s detaljima aplikacije",
"disablePageTransitions": "Ugasite animaciju prijelaza stranice",
"reversePageTransitions": "Reverzne animacije prijelaza stranice",
"minStarCount": "Minimum Star Count",
"addInfoBelow": "Add this info below.",
"addInfoInSettings": "Add this info in the Settings.",
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"minStarCount": "Najmanji broj zvjezdica",
"addInfoBelow": "Dodajte ove informacije ispod.",
"addInfoInSettings": "Dodajte ove informacije u Postavkama.",
"githubSourceNote": "GitHub ograničavanje se može izbjeći korišćenjem tokena za lični pristup.",
"gitlabSourceNote": "GitLab APK preuzimanje možda neće raditi bez tokena za lični pristup.",
"sortByFileNamesNotLinks": "Sortirajte po imenima datoteka umjesto po punim linkovima",
"filterReleaseNotesByRegEx": "Filtirajte promjene u izdanju po regularnom izrazu",
"customLinkFilterRegex": "Prilagođeni APK link filtrira se po regularnom izrazu (Zadano '.apk$')",
"appsPossiblyUpdated": "Pokušano ažuriranje aplikacija",
"appsPossiblyUpdatedNotifDescription": "Obavještava korisnika da je ažuriranje jedne ili više aplikacija potencijalno izvršeno u pozadini",
"xWasPossiblyUpdatedToY": "{} aplikacija bi trebala biti ažurirana na {}.",
"enableBackgroundUpdates": "Dozvolite ažuriranja u pozadini",
"backgroundUpdateReqsExplanation": "Ažuriranja u pozadini možda neće raditi za sve aplikacije.",
"backgroundUpdateLimitsExplanation": "Uspjeh ažuriranja u pozadini se može provjeriti tek kada otvorite Obtainium.",
"verifyLatestTag": "Provjerite 'posljednu' ('latest') oznaku",
"intermediateLinkRegex": "Filtrirajte da prvo posjetite 'Intemediate' link",
"intermediateLinkNotFound": "Intermediate link nije nađen",
"exemptFromBackgroundUpdates": "Izuzmi iz ažuriranja u pozadini (ako su uključeni)",
"bgUpdatesOnWiFiOnly": "Isključite ažuriranje u pozadini kada niste na WiFi-ju",
"autoSelectHighestVersionCode": "Automatski izaberite najveću (verziju) versionCode APK-a",
"versionExtractionRegEx": "RegEx ekstrakcija verzije",
"matchGroupToUse": "Podjesite grupu za upotebu",
"highlightTouchTargets": "Istaknite manje vidljive touch mete",
"pickExportDir": "Izaberite datoteku za izvoz",
"autoExportOnChanges": "Automatski izvezite pri promjenama",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtrirajte verzije po regulatnom izrazu",
"trySelectingSuggestedVersionCode": "Probajte izabrati preloženu (verziju) versionCode APK-a",
"dontSortReleasesList": "Zadrži redosled izdanja iz API-a",
"reverseSort": "Obrni redosled",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Meni za otkrivanje grešaka",
"bgTaskStarted": "Rad u pozadini pokrenut - provjerite log-ove.",
"runBgCheckNow": "Pokrenite pozadinsku provjeru ažuriranja sad",
"versionExtractWholePage": "Primjenite Regex ekstrakciju verzije na cijelu stranicu",
"installing": "Instaliranje",
"skipUpdateNotifications": "Ne prikazujte obavještenja ažuriranja",
"updatesAvailableNotifChannel": "Dostupna ažuriranja",
"appsUpdatedNotifChannel": "Aplikacije su ažurirane",
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
"appsPossiblyUpdatedNotifChannel": "Pokušano ažuriranje aplikacija",
"errorCheckingUpdatesNotifChannel": "Greška pri provjeri ažuriranja",
"appsRemovedNotifChannel": "Aplikacije su uklonjene",
"downloadingXNotifChannel": "Preuzimanje {}",
"completeAppInstallationNotifChannel": "Dovršite instalaciju aplikacije",
"checkingForUpdatesNotifChannel": "Tražim moguće nadogradnje",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"onlyCheckInstalledOrTrackOnlyApps": "Isključivo provjerite ažuriranje za instalirane i aplikacije 'samo za praćenje'",
"supportFixedAPKURL": "Podržite fiksne APK URL-ove",
"selectX": "Izaberite {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Želite li ukloniti aplikaciju?",
"other": "Želite li ukloniti aplikacije?"
@@ -326,7 +330,7 @@
"other": "{} i još {} aplikacija je ažurirano."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
"one": "{} i još jedna aplikacija je vjerovatno ažurirana.",
"other": "{} i još {} aplikacija su vjerovatno ažurirane."
}
}

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
"pickExportDir": "Vybrat adresář pro export",
"autoExportOnChanges": "Automatický export při změnách",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu",
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Odstranit Apku?",
"other": "Odstranit Apky?"

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben",
"pickExportDir": "Export-Verzeichnis wählen",
"autoExportOnChanges": "Automatischer Export bei Änderung(en)",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
"trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
"dontSortReleasesList": "Freigaberelease von der API ordern",
"reverseSort": "Umgekehrtes Sortieren",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Debug-Menü",
"bgTaskStarted": "Hintergrundaufgabe gestartet Logs prüfen.",
"runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
@@ -275,8 +278,9 @@
"completeAppInstallationNotifChannel": "App Installation abschließen",
"checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen",
"onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt",
"selectX": "Wähle {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "App entfernen?",
"other": "Apps entfernen?"
@@ -329,4 +333,4 @@
"one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
"other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
}
}
}

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Remove App?",
"other": "Remove Apps?"

View File

@@ -14,8 +14,8 @@
"githubPATLabel": "Token de Acceso Personal de GitHub (Reduce tiempos de espera)",
"includePrereleases": "Incluir versiones preliminares",
"fallbackToOlderReleases": "Retorceder a versiones previas",
"filterReleaseTitlesByRegEx": "Filtra Títulos de Versiones mediantes Expresiones Regulares",
"invalidRegEx": "Expresión regular inválida",
"filterReleaseTitlesByRegEx": "Filtrar Títulos de Versiones",
"invalidRegEx": "Expresión inválida",
"noDescription": "Sin descripción",
"cancel": "Cancelar",
"continue": "Continuar",
@@ -30,7 +30,7 @@
"app": "Aplicación",
"appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'Solo Seguimiento'.",
"youPickedTrackOnly": "Debes seleccionar la opción de 'Solo Seguimiento'.",
"trackOnlyAppDescription": "Se monitorizará la aplicación en busca de actualizaciones, pero Obtainium no será capaz de descargarla o acutalizarla.",
"trackOnlyAppDescription": "Se monitorizará la aplicación en busca de actualizaciones, pero Obtainium no será capaz de descargarla o actalizarla.",
"cancelled": "Cancelado",
"appAlreadyAdded": "Aplicación ya añadida",
"alreadyUpToDateQuestion": "¿Aplicación ya actualizada?",
@@ -61,7 +61,7 @@
"removeSelectedApps": "Borrar Aplicaciones Seleccionadas",
"updateX": "Actualizar {}",
"installX": "Instalar {}",
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo Seguimient)\ncomo Actualizada",
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo Seguimiento)\ncomo Actualizada",
"changeX": "Cambiar {}",
"installUpdateApps": "Instalar/Actualizar Aplicaciones",
"installUpdateSelectedApps": "Instalar/Actualizar Aplicaciones Seleccionadas",
@@ -72,7 +72,7 @@
"pinToTop": "Fijar arriba",
"unpinFromTop": "Desfijar de arriba",
"resetInstallStatusForSelectedAppsQuestion": "¿Restuarar Estado de Instalación para las Aplicaciones Seleccionadas?",
"installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de utilidad cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
"installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
"shareSelectedAppURLs": "Compartir URLs de las Aplicaciones Seleccionadas",
"resetInstallStatus": "Restaurar Estado de Instalación",
"more": "Más",
@@ -100,7 +100,7 @@
"noResults": "Resultados no encontrados",
"importX": "Importar {}",
"importedAppsIdDisclaimer": "Las Aplicaciones Importadas pueden mostrarse incorrectamente como \"No Instalada\".\nPara arreglar esto, reinstálalas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a las URLs y a los métodos de importación mediante terceros.",
"importErrors": "Import Errors",
"importErrors": "Errores de Importación",
"importedXOfYApps": "{} de {} Aplicaciones importadas.",
"followingURLsHadErrors": "Las siguientes URLs tuvieron problemas:",
"okay": "Correcto",
@@ -135,7 +135,7 @@
"share": "Compartir",
"appNotFound": "Aplicación no encontrada",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Elige una APK",
"pickAnAPK": "Selecciona una APK",
"appHasMoreThanOnePackage": "{} tiene más de un paquete:",
"deviceSupportsXArch": "Tu dispositivo soporta las siguientes arquitecturas de procesador: {}.",
"deviceSupportsFollowingArchs": "Tu dispositivo soporta las siguientes arquitecturas de procesador:",
@@ -154,8 +154,8 @@
"appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas",
"xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}",
"completeAppInstallation": "Instalación Completa de la Aplicación",
"obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierta para instalar aplicaciones",
"completeAppInstallationNotifDescription": "Pide al usuario volver a Obtainium para teminar de instalar una aplicación",
"obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierto para instalar aplicaciones",
"completeAppInstallationNotifDescription": "Pide al usuario volver a Obtainium para terminar de instalar una aplicación",
"checkingForUpdates": "Buscando Actualizaciones",
"checkingForUpdatesNotifDescription": "Notificación temporal que aparece al buscar actualizaciones",
"pleaseAllowInstallPerm": "Por favor, permite a Obtainium instalar aplicaciones",
@@ -180,14 +180,14 @@
"steamMobile": "Steam Mobile",
"steamChat": "Steam Chat",
"install": "Instalar",
"markInstalled": "Marcar como Instalda",
"markInstalled": "Marcar como Instalada",
"update": "Actualizar",
"markUpdated": "Marcar como Actualizada",
"additionalOptions": "Opciones Adicionales",
"disableVersionDetection": "Descativar Detección de Versiones",
"noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda no funcionar correctamente.",
"downloadingX": "Descargando {}",
"downloadNotifDescription": "Notifica al usuario de progreso de descarga de una aplicación",
"downloadNotifDescription": "Notifica al usuario del progreso de descarga de una aplicación",
"noAPKFound": "APK no encontrada",
"noVersionDetection": "Sin detección de versiones",
"categorize": "Catogorizar",
@@ -196,14 +196,14 @@
"noCategory": "Sin Categoría",
"noCategories": "Sin Categorías",
"deleteCategoriesQuestion": "¿Borrar Categorías?",
"categoryDeleteWarning": "Todas las aplicaciones en las categorías borradas serán margadas como 'Sin Categoría'.",
"categoryDeleteWarning": "Todas las aplicaciones en las categorías borradas serán marcadas como 'Sin Categoría'.",
"addCategory": "Añadir Categoría",
"label": "Nombre",
"language": "Idioma",
"copiedToClipboard": "Copiado al Portapapeles",
"storagePermissionDenied": "Permiso de Almacenamiento rechazado",
"selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaicones seleccionadas.",
"filterAPKsByRegEx": "Filtrar APKs mediante Expresiones Regulares",
"selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaciones seleccionadas.",
"filterAPKsByRegEx": "Filtrar por APKs",
"removeFromObtainium": "Eliminar de Obtainium",
"uninstallFromDevice": "Desinstalar del Dispositivo",
"onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.",
@@ -211,72 +211,76 @@
"releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.",
"changes": "Cambios",
"releaseDate": "Fecha de Publicación",
"importFromURLsInFile": "Importar de URls en un Archivo (como OPML)",
"importFromURLsInFile": "Importar de URls desde un Archivo (como OPML)",
"versionDetection": "Detección de Versiones",
"standardVersionDetection": "Detección de versiones estándar",
"groupByCategory": "Agrupar por Categoría",
"autoApkFilterByArch": "Tratar de filtrar las APKs mediante arquitecturas de procesador si es posible",
"autoApkFilterByArch": "Filtrar las APKs mediante arquitecturas de procesador, si es posible",
"overrideSource": "Sobrescribir Fuente",
"dontShowAgain": "No mostrar de nuevo",
"dontShowTrackOnlyWarnings": "No mostrar avisos de 'Solo Seguimiento'",
"dontShowAPKOriginWarnings": "No mostrar avisos de las fuentes de las APks",
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
"about": "About",
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
"checkOnStart": "Check for updates on startup",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
"pickHighestVersionCode": "Auto-select highest version code APK",
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page",
"disablePageTransitions": "Disable page transition animations",
"reversePageTransitions": "Reverse page transition animations",
"minStarCount": "Minimum Star Count",
"addInfoBelow": "Add this info below.",
"addInfoInSettings": "Add this info in the Settings.",
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"moveNonInstalledAppsToBottom": "Mover las Apps no instaladas al final de la vista de Apps",
"gitlabPATLabel": "Token GitLab de Acceso Personal\n(Habilita la Búsqueda y Mejor Detección de APKs)",
"about": "Acerca",
"requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en Ajustes)",
"checkOnStart": "Comprobar actualizaciones durante el inicio",
"tryInferAppIdFromCode": "Intentar deducir la ID de la APP por el código fuente",
"removeOnExternalUninstall": "Auto eliminar Apps desinstaladas externamente",
"pickHighestVersionCode": "Auto selección versión superior del código APK",
"checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la App",
"disablePageTransitions": "Deshabilitar animaciones de transición de la página",
"reversePageTransitions": "Invertir las animaciones de transición de la página",
"minStarCount": "Número Mínimo de Estrellas",
"addInfoBelow": "Añadir esta información debajo.",
"addInfoInSettings": "Añadir esta información en Ajustes.",
"githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con una clave API.",
"gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin una clave API.",
"sortByFileNamesNotLinks": "Ordenar por nombres de fichero en vez de por enlaces completos",
"filterReleaseNotesByRegEx": "Filtrar por Notas de Versión (Release Notes)",
"customLinkFilterRegex": "Filtro personalizado de Enlace APK (por defecto '.apk$')",
"appsPossiblyUpdated": "Actualización de Apps intentada",
"appsPossiblyUpdatedNotifDescription": "Notifica al usuario que las actualizaciones en segundo plano podrían haberse realizado para una o más aplicaciones",
"xWasPossiblyUpdatedToY": "{} podría estar actualizada a {}.",
"enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano",
"backgroundUpdateReqsExplanation": "Las actualizaciones en segundo plano pueden no estar disponibles para todas las aplicaciones.",
"backgroundUpdateLimitsExplanation": "El éxito de las instalaciones en segundo plano solo se puede verificar con Obtainium abierto.",
"verifyLatestTag": "Verifica la etiqueta 'latest'",
"intermediateLinkRegex": "Filtrar por Enlace 'Intermedio' para Visitar Primero",
"intermediateLinkNotFound": "Enlace Intermedio no encontrado",
"exemptFromBackgroundUpdates": "Exento de actualizciones en segundo plano (si están habilitadas)",
"bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi",
"autoSelectHighestVersionCode": "Auto Selección de la versionCode APK superior",
"versionExtractionRegEx": "Versión de Extracción de RegEx",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"highlightTouchTargets": "Resaltar objetivos menos obvios",
"pickExportDir": "Selecciona el Directorio para Exportar",
"autoExportOnChanges": "Auto Exportar cuando haya cambios",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtrar por Versiones",
"trySelectingSuggestedVersionCode": "Prueba seleccionando la versionCode APK sugerida",
"dontSortReleasesList": "Mantener el order de publicación desde API",
"reverseSort": "Orden inverso",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Menu Depurar",
"bgTaskStarted": "Iniciada tarea en segundo plano - revisa los logs.",
"runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano",
"versionExtractWholePage": "Aplicar la Versión de Extracción Regex a la Página Entera",
"installing": "Instalando",
"skipUpdateNotifications": "Omitir notificaciones sobre actualizaciones",
"updatesAvailableNotifChannel": "Actualizaciones Disponibles",
"appsUpdatedNotifChannel": "Aplicaciones Actualizadas",
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
"appsPossiblyUpdatedNotifChannel": "Se ha Intentado Actualizar la Aplicación",
"errorCheckingUpdatesNotifChannel": "Error Buscando Actualizaciones",
"appsRemovedNotifChannel": "Aplicaciones Eliminadas",
"downloadingXNotifChannel": "Descargando {}",
"completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación",
"checkingForUpdatesNotifChannel": "Buscando Actualizaciones",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"supportFixedAPKURL": "Soporte para URLs fijas de APK",
"selectX": "Selecciona {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "¿Eliminar Aplicación?",
"other": "¿Eliminar Aplicaciones?"
@@ -319,14 +323,14 @@
},
"xAndNMoreUpdatesAvailable": {
"one": "{} y 1 aplicación más tiene actualizaciones.",
"other": "{} y {} aplicaciones más tiene actualizaciones."
"other": "{} y {} aplicaciones más tienen actualizaciones."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} y 1 aplicación más han sido actualizadas.",
"other": "{} y {} aplicaciones más han sido actualizadas."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
"one": "{} y 1 aplicación más podría haber sido actualizada.",
"other": "{} y {} aplicaciones más podrían haber sido actualizadas."
}
}
}

View File

@@ -239,44 +239,48 @@
"sortByFileNamesNotLinks": "مرتب سازی بر اساس نام فایل به جای پیوندهای کامل",
"filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید",
"customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیش‌فرض '.apk$')",
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"appsPossiblyUpdated": "به‌روزرسانی برنامه انجام شد",
"appsPossiblyUpdatedNotifDescription": "به کاربر اطلاع می‌دهد که به‌روزرسانی‌های یک یا چند برنامه به طور بالقوه در پس‌زمینه اعمال شده است",
"xWasPossiblyUpdatedToY": "ممکن است {} به {} به روز شده باشد.",
"enableBackgroundUpdates": "به روز رسانی پس زمینه را فعال کنید",
"backgroundUpdateReqsExplanation": "به روز رسانی پس زمینه ممکن است برای همه برنامه ها امکان پذیر نباشد.",
"backgroundUpdateLimitsExplanation": "موفقیت نصب پس‌زمینه تنها زمانی مشخص می‌شود که Obtainium باز شود.",
"verifyLatestTag": "برچسب \"آخرین\" را تأیید کنید",
"intermediateLinkRegex": "برای اولین بار بازدید از لینک \"متوسط\" را فیلتر کنید",
"intermediateLinkNotFound": "لینک میانی پیدا نشد",
"exemptFromBackgroundUpdates": "معاف از به‌روزرسانی‌های پس‌زمینه (در صورت فعال بودن)",
"bgUpdatesOnWiFiOnly": "به‌روزرسانی‌های پس‌زمینه را در صورت عدم اتصال به WiFi غیرفعال کنید",
"autoSelectHighestVersionCode": "انتخاب خودکار بالاترین نسخه کد APK",
"versionExtractionRegEx": "نسخه استخراج RegEx",
"matchGroupToUse": "گروه مورد استفاده را مطابقت دهید",
"highlightTouchTargets": "اهداف لمسی کمتر واضح را برجسته کنید",
"pickExportDir": "فهرست صادرات را انتخاب کنید",
"autoExportOnChanges": "صادرات خودکار تغییرات",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "فیلتر کردن نسخه ها با RegEx",
"trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید",
"dontSortReleasesList": "حفظ سفارش انتشار از API",
"reverseSort": "مرتب سازی معکوس",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "منوی اشکال زدایی",
"bgTaskStarted": "کار پس زمینه شروع شد - لاگ های مربوط را بررسی کنید.",
"runBgCheckNow": "اکنون به‌روزرسانی پس‌زمینه را بررسی کنید",
"versionExtractWholePage": "نسخه Extraction Regex را در کل صفحه اعمال کنید",
"installing": "در حال نصب",
"skipUpdateNotifications": "رد شدن از اعلان های به روز رسانی",
"updatesAvailableNotifChannel": "بروزرسانی در دسترس ",
"appsUpdatedNotifChannel": "برنامه ها به روز شدند",
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
"appsPossiblyUpdatedNotifChannel": "به‌روزرسانی برنامه انجام شد",
"errorCheckingUpdatesNotifChannel": "خطا در بررسی به‌روزرسانی‌ها",
"appsRemovedNotifChannel": "برنامه ها حذف شدند",
"downloadingXNotifChannel": "در حال دانلود {}",
"completeAppInstallationNotifChannel": "نصب کامل برنامه",
"checkingForUpdatesNotifChannel": "بررسی به‌روزرسانی‌ها",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"onlyCheckInstalledOrTrackOnlyApps": "فقط برنامه های نصب شده و فقط ردیابی را برای به روز رسانی بررسی کنید",
"supportFixedAPKURL": "پشتیبانی از URL های APK ثابت",
"selectX": "انتخاب کنید {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟"
@@ -326,7 +330,7 @@
"other": "{} و {} برنامه دیگر به روز شدند."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
"one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.",
"other": "ممکن است {} و {} برنامه های دیگر به روز شده باشند."
}
}
}

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Supprimer l'application ?",
"other": "Supprimer les applications ?"

View File

@@ -215,7 +215,7 @@
"versionDetection": "Verzió érzékelés",
"standardVersionDetection": "Alapért. verzió érzékelés",
"groupByCategory": "Csoportosítás Kategória alapján",
"autoApkFilterByArch": "Ha lehetséges, próbálja CPU architektúra szerint szűrni az APK-okat",
"autoApkFilterByArch": "Ha lehetséges, próbálja CPU architektúra szerint szűrni az APK-kat",
"overrideSource": "Forrás felülbírálása",
"dontShowAgain": "Ne mutassa ezt újra",
"dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
@@ -255,10 +255,13 @@
"highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
"pickExportDir": "Válassza az Exportálási könyvtárat",
"autoExportOnChanges": "Auto-exportálás a változások után",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
"dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
"reverseSort": "Fordított rendezés",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Hibakereső menü",
"bgTaskStarted": "A háttérfeladat elindult ellenőrizze a naplókat.",
"enableBackgroundUpdates": "Frissítések a háttérben",
@@ -275,8 +278,9 @@
"completeAppInstallationNotifChannel": "Teljes app telepítés",
"checkingForUpdatesNotifChannel": "Frissítések keresése",
"onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak követhető appokat ellenőrizze frissítésekért",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"supportFixedAPKURL": "Támogatja a rögzített APK URL-eket",
"selectX": "Kiválaszt {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Eltávolítja az alkalmazást?",
"other": "Eltávolítja az alkalmazást?"

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi",
"pickExportDir": "Scegli cartella esp.",
"autoExportOnChanges": "Auto-esporta dopo modifiche",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtra versioni con espressione regolare",
"trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito",
"dontSortReleasesList": "Conserva l'ordine di release da API",
"reverseSort": "Ordine inverso",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Menu di debug",
"bgTaskStarted": "Attività in secondo piano iniziata - controllo log.",
"runBgCheckNow": "Inizia aggiornamento in secondo piano ora",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "Cerca aggiornamenti solo per app installate e app in Solo-Monitoraggio",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Rimuovere l'app?",
"other": "Rimuovere le app?"

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
"pickExportDir": "エクスポートディレクトリを選択",
"autoExportOnChanges": "変更があった際に自動でエクスポートする",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
"dontSortReleasesList": "APIからのリリース順を保持する",
"reverseSort": "逆順ソート",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "デバッグメニュー",
"bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。",
"runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "アプリを削除しますか?",
"other": "アプリを削除しますか?"

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.",
"pickExportDir": "Kies de exportmap",
"autoExportOnChanges": "Automatisch exporteren bij wijzigingen",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filter versies met een reguliere expressie",
"trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
"dontSortReleasesList": "Volgorde van releases behouden vanuit de API",
"reverseSort": "Sortering omkeren",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Debug menu",
"bgTaskStarted": "Achtergrondtaak gestart - controleer de logs.",
"runBgCheckNow": "Voer nu een achtergrondupdatecontrole uit",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "App verwijderen?",
"other": "Apps verwijderen?"

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
"pickExportDir": "Wybierz katalog eksportu",
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
"dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
"reverseSort": "Odwrotne sortowanie",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Menu debugowania",
"bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.",
"runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle",
@@ -275,8 +278,9 @@
"completeAppInstallationNotifChannel": "Ukończenie instalacji aplikacji",
"checkingForUpdatesNotifChannel": "Sprawdzanie dostępności aktualizacji",
"onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
"selectX": "Wybierz {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Usunąć aplikację?",
"few": "Usunąć aplikacje?",

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Destaque areas de toque menos óbvias",
"pickExportDir": "Escolher Diretorio de Exportação",
"autoExportOnChanges": "Auto-exportar em mudanças",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtrar Versões por Expressão Regular",
"trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
"dontSortReleasesList": "Reter a ordem de lançamento da API",
"reverseSort": "Ordenação reversa",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Menu Debug",
"bgTaskStarted": "Tarefa em segundo plano iniciada - verifique os logs.",
"runBgCheckNow": "Execute a verificação de atualização em segundo plano agora",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Remover App?",
"other": "Remover Apps?"

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
"pickExportDir": "Выбрать каталог для экспорта",
"autoExportOnChanges": "Автоэкспорт при изменениях",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
"dontSortReleasesList": "Сохранить порядок релизов от API",
"reverseSort": "Обратная сортировка",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Меню отладки",
"bgTaskStarted": "Фоновая задача начата — проверьте журналы",
"runBgCheckNow": "Запустить проверку фонового обновления сейчас",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Удалить приложение?",
"other": "Удалить приложения?"

View File

@@ -256,13 +256,17 @@
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Välj Exportsökväg",
"autoExportOnChanges": "Automatisk export vid ändringar",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Omvänd sortering",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Felsökningsmeny",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Kör Bakgrundsuppdateringskoll Nu",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Ta Bort App?",
"other": "Ta Bort Appar?"

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula",
"pickExportDir": "Dışa Aktarılacak Klasörü Seç",
"autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele",
"trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
"dontSortReleasesList": "API'den sıralama düzenini koru",
"reverseSort": "Ters sıralama",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Hata Ayıklama Menüsü",
"bgTaskStarted": "Arka plan görevi başladı - günlükleri kontrol et.",
"runBgCheckNow": "Arka Plan Güncelleme Kontrolünü Şimdi Çalıştır",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "Uygulamayı Kaldır?",
"other": "Uygulamaları Kaldır?"

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn",
"pickExportDir": "Chọn thư mục xuất",
"autoExportOnChanges": "Tự động xuất khi thay đổi",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Lọc phiên bản theo biểu thức chính quy",
"trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
"dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
"reverseSort": "Sắp xếp ngược",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "Danh sách gỡ lỗi",
"bgTaskStarted": "Tác vụ nền đã bắt đầu - kiểm tra nhật ký.",
"runBgCheckNow": "Chạy kiểm tra cập nhật nền ngay bây giờ",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra các ứng dụng đã cài đặt và Chỉ-Theo dõi để biết các bản cập nhật",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion":{
"one": "Gỡ ứng dụng?",
"other": "Gỡ ứng dụng?"

View File

@@ -256,10 +256,13 @@
"highlightTouchTargets": "突出展示不明显的触摸区域",
"pickExportDir": "选择导出文件夹",
"autoExportOnChanges": "数据变更时自动导出",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "筛选版本号(正则表达式)",
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
"dontSortReleasesList": "保持来自 API 的发行顺序",
"reverseSort": "反转排序",
"takeFirstLink": "Take first link",
"skipSort": "Skip sorting",
"debugMenu": "调试选项",
"bgTaskStarted": "后台任务已启动 - 详见日志",
"runBgCheckNow": "立即进行后台更新检查",
@@ -277,6 +280,7 @@
"onlyCheckInstalledOrTrackOnlyApps": "只对已安装和“仅追踪”的应用进行更新检查",
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"removeAppQuestion": {
"one": "是否删除应用?",
"other": "是否删除应用?"

View File

@@ -65,7 +65,7 @@ class FDroid extends AppSource {
) async {
String? appId = await tryInferringAppId(standardUrl);
String host = Uri.parse(standardUrl).host;
return getAPKUrlsFromFDroidPackagesAPIResponse(
var details = getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest('https://$host/api/v1/packages/$appId'),
'https://$host/repo/$appId',
standardUrl,
@@ -80,6 +80,23 @@ class FDroid extends AppSource {
true
? additionalSettings['filterVersionsByRegEx']
: null);
if (!hostChanged) {
try {
var res = await sourceRequest(
'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml');
String author = res.body
.split('\n')
.where((l) => l.startsWith('AuthorName: '))
.first
.split(': ')
.sublist(1)
.join(': ');
details.names.author = author;
} catch (e) {
// Fail silently
}
}
return details;
}
@override
@@ -111,79 +128,79 @@ class FDroid extends AppSource {
throw getObtainiumHttpError(res);
}
}
}
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
{bool autoSelectHighestVersionCode = false,
bool trySelectingSuggestedVersionCode = false,
String? filterVersionsByRegEx}) {
if (res.statusCode == 200) {
var response = jsonDecode(res.body);
List<dynamic> releases = response['packages'] ?? [];
if (releases.isEmpty) {
throw NoReleasesError();
}
String? version;
Iterable<dynamic> releaseChoices = [];
// Grab the versionCode suggested if the user chose to do that
// Only do so at this stage if the user has no release filter
if (trySelectingSuggestedVersionCode &&
response['suggestedVersionCode'] != null &&
filterVersionsByRegEx == null) {
var suggestedReleases = releases.where((element) =>
element['versionCode'] == response['suggestedVersionCode']);
if (suggestedReleases.isNotEmpty) {
releaseChoices = suggestedReleases;
version = suggestedReleases.first['versionName'];
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
{bool autoSelectHighestVersionCode = false,
bool trySelectingSuggestedVersionCode = false,
String? filterVersionsByRegEx}) {
if (res.statusCode == 200) {
var response = jsonDecode(res.body);
List<dynamic> releases = response['packages'] ?? [];
if (releases.isEmpty) {
throw NoReleasesError();
}
}
// Apply the release filter if any
if (filterVersionsByRegEx?.isNotEmpty == true) {
version = null;
releaseChoices = [];
for (var i = 0; i < releases.length; i++) {
if (RegExp(filterVersionsByRegEx!)
.hasMatch(releases[i]['versionName'])) {
version = releases[i]['versionName'];
}
}
if (version == null) {
throw NoVersionError();
}
}
// Default to the highest version
version ??= releases[0]['versionName'];
if (version == null) {
throw NoVersionError();
}
// If a suggested release was not already picked, pick all those with the selected version
if (releaseChoices.isEmpty) {
releaseChoices =
releases.where((element) => element['versionName'] == version);
}
// For the remaining releases, use the toggles to auto-select one if possible
if (releaseChoices.length > 1) {
if (autoSelectHighestVersionCode) {
releaseChoices = [releaseChoices.first];
} else if (trySelectingSuggestedVersionCode &&
response['suggestedVersionCode'] != null) {
var suggestedReleases = releaseChoices.where((element) =>
String? version;
Iterable<dynamic> releaseChoices = [];
// Grab the versionCode suggested if the user chose to do that
// Only do so at this stage if the user has no release filter
if (trySelectingSuggestedVersionCode &&
response['suggestedVersionCode'] != null &&
filterVersionsByRegEx == null) {
var suggestedReleases = releases.where((element) =>
element['versionCode'] == response['suggestedVersionCode']);
if (suggestedReleases.isNotEmpty) {
releaseChoices = suggestedReleases;
version = suggestedReleases.first['versionName'];
}
}
// Apply the release filter if any
if (filterVersionsByRegEx?.isNotEmpty == true) {
version = null;
releaseChoices = [];
for (var i = 0; i < releases.length; i++) {
if (RegExp(filterVersionsByRegEx!)
.hasMatch(releases[i]['versionName'])) {
version = releases[i]['versionName'];
}
}
if (version == null) {
throw NoVersionError();
}
}
// Default to the highest version
version ??= releases[0]['versionName'];
if (version == null) {
throw NoVersionError();
}
// If a suggested release was not already picked, pick all those with the selected version
if (releaseChoices.isEmpty) {
releaseChoices =
releases.where((element) => element['versionName'] == version);
}
// For the remaining releases, use the toggles to auto-select one if possible
if (releaseChoices.length > 1) {
if (autoSelectHighestVersionCode) {
releaseChoices = [releaseChoices.first];
} else if (trySelectingSuggestedVersionCode &&
response['suggestedVersionCode'] != null) {
var suggestedReleases = releaseChoices.where((element) =>
element['versionCode'] == response['suggestedVersionCode']);
if (suggestedReleases.isNotEmpty) {
releaseChoices = suggestedReleases;
}
}
}
if (releaseChoices.isEmpty) {
throw NoReleasesError();
}
List<String> apkUrls = releaseChoices
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
.toList();
return APKDetails(version, getApkUrlsFromUrls(apkUrls.toSet().toList()),
AppNames(sourceName, Uri.parse(standardUrl).pathSegments.last));
} else {
throw getObtainiumHttpError(res);
}
if (releaseChoices.isEmpty) {
throw NoReleasesError();
}
List<String> apkUrls = releaseChoices
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
.toList();
return APKDetails(version, getApkUrlsFromUrls(apkUrls.toSet().toList()),
AppNames(sourceName, Uri.parse(standardUrl).pathSegments.last));
} else {
throw getObtainiumHttpError(res);
}
}

View File

@@ -234,7 +234,7 @@ class GitHub extends AppSource {
bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
bool dontSortReleasesList =
additionalSettings['dontSortReleasesList'] == true;
String? latestTag;
dynamic latestRelease;
if (verifyLatestTag) {
var temp = requestUrl.split('?');
Response res = await sourceRequest(
@@ -245,12 +245,20 @@ class GitHub extends AppSource {
}
throw getObtainiumHttpError(res);
}
var jsres = jsonDecode(res.body);
latestTag = jsres['tag_name'] ?? jsres['name'];
latestRelease = jsonDecode(res.body);
}
Response res = await sourceRequest(requestUrl);
if (res.statusCode == 200) {
var releases = jsonDecode(res.body) as List<dynamic>;
if (latestRelease != null) {
var latestTag = latestRelease['tag_name'] ?? latestRelease['name'];
if (releases
.where((element) =>
(element['tag_name'] ?? element['name']) == latestTag)
.isEmpty) {
releases = [latestRelease, ...releases];
}
}
List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
(release['assets'] as List<dynamic>?)
@@ -299,13 +307,13 @@ class GitHub extends AppSource {
}
});
}
if (latestTag != null &&
if (latestRelease != null &&
releases.isNotEmpty &&
latestTag !=
latestRelease !=
(releases[releases.length - 1]['tag_name'] ??
releases[0]['name'])) {
var ind = releases.indexWhere(
(element) => latestTag == (element['tag_name'] ?? element['name']));
var ind = releases.indexWhere((element) =>
latestRelease == (element['tag_name'] ?? element['name']));
if (ind >= 0) {
releases.add(releases.removeAt(ind));
}

View File

@@ -48,12 +48,6 @@ class GitLab extends AppSource {
label: tr('fallbackToOlderReleases'), defaultValue: true)
]
];
searchQuerySettingFormItems = [
GeneratedFormTextField('PAT',
label: tr('gitlabPATLabel').split('(')[0],
password: true,
required: false)
];
}
@override
@@ -86,18 +80,8 @@ class GitLab extends AppSource {
@override
Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async {
String? PAT;
if (!hostChanged) {
PAT = await getPATIfAny({});
if (PAT == null) {
throw CredsNeededError(name);
}
}
if ((querySettings['PAT'] as String?)?.isNotEmpty == true) {
PAT = querySettings['PAT'];
}
var url =
'https://$host/api/v4/search?${PAT?.isNotEmpty == true ? 'private_token=$PAT&' : ''}scope=projects&search=${Uri.encodeQueryComponent(query)}';
'https://$host/api/v4/projects?search=${Uri.encodeQueryComponent(query)}';
var res = await sourceRequest(url);
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
@@ -203,7 +187,7 @@ class GitLab extends AppSource {
});
}
if (apkDetailsList.isEmpty) {
throw NoReleasesError();
throw NoReleasesError(note: tr('gitlabSourceNote'));
}
if (fallbackToOlderReleases) {
if (additionalSettings['trackOnly'] != true) {
@@ -211,7 +195,7 @@ class GitLab extends AppSource {
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
}
if (apkDetailsList.isEmpty) {
throw NoReleasesError();
throw NoReleasesError(note: tr('gitlabSourceNote'));
}
}
return apkDetailsList.first;

View File

@@ -94,7 +94,8 @@ class HTML extends AppSource {
GeneratedFormSwitch('sortByFileNamesNotLinks',
label: tr('sortByFileNamesNotLinks'))
],
[GeneratedFormSwitch('reverseSort', label: tr('reverseSort'))],
[GeneratedFormSwitch('skipSort', label: tr('skipSort'))],
[GeneratedFormSwitch('reverseSort', label: tr('takeTopLink'))],
[
GeneratedFormSwitch('supportFixedAPKURL',
defaultValue: true, label: tr('supportFixedAPKURL')),
@@ -185,12 +186,15 @@ class HTML extends AppSource {
.toList();
}
List<String> links = [];
bool skipSort = additionalSettings['skipSort'] == true;
if ((additionalSettings['intermediateLinkRegex'] as String?)
?.isNotEmpty ==
true) {
var reg = RegExp(additionalSettings['intermediateLinkRegex']);
links = allLinks.where((element) => reg.hasMatch(element)).toList();
links.sort((a, b) => compareAlphaNumeric(a, b));
if (!skipSort) {
links.sort((a, b) => compareAlphaNumeric(a, b));
}
if (links.isEmpty) {
throw ObtainiumError(tr('intermediateLinkNotFound'));
}
@@ -211,10 +215,14 @@ class HTML extends AppSource {
Uri.parse(element).path.toLowerCase().endsWith('.apk'))
.toList();
}
links.sort((a, b) => additionalSettings['sortByFileNamesNotLinks'] == true
? compareAlphaNumeric(a.split('/').where((e) => e.isNotEmpty).last,
b.split('/').where((e) => e.isNotEmpty).last)
: compareAlphaNumeric(a, b));
if (!skipSort) {
links.sort((a, b) =>
additionalSettings['sortByFileNamesNotLinks'] == true
? compareAlphaNumeric(
a.split('/').where((e) => e.isNotEmpty).last,
b.split('/').where((e) => e.isNotEmpty).last)
: compareAlphaNumeric(a, b));
}
if (additionalSettings['reverseSort'] == true) {
links = links.reversed.toList();
}

View File

@@ -40,7 +40,7 @@ class IzzyOnDroid extends AppSource {
Map<String, dynamic> additionalSettings,
) async {
String? appId = await tryInferringAppId(standardUrl);
return getAPKUrlsFromFDroidPackagesAPIResponse(
return fd.getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest(
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
'https://android.izzysoft.de/frepo/$appId',

View File

@@ -34,7 +34,9 @@ class CredsNeededError extends ObtainiumError {
}
class NoReleasesError extends ObtainiumError {
NoReleasesError() : super(tr('noReleaseFound'));
NoReleasesError({String? note})
: super(
'${tr('noReleaseFound')}${note?.isNotEmpty == true ? '\n\n$note' : ''}');
}
class NoAPKError extends ObtainiumError {

View File

@@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports
import 'package:easy_localization/src/localization.dart';
const String currentVersion = '0.14.35';
const String currentVersion = '0.14.40';
const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES

View File

@@ -21,10 +21,10 @@ class AddAppPage extends StatefulWidget {
const AddAppPage({super.key});
@override
State<AddAppPage> createState() => _AddAppPageState();
State<AddAppPage> createState() => AddAppPageState();
}
class _AddAppPageState extends State<AddAppPage> {
class AddAppPageState extends State<AddAppPage> {
bool gettingAppInfo = false;
bool searching = false;
@@ -36,9 +36,62 @@ class _AddAppPageState extends State<AddAppPage> {
bool additionalSettingsValid = true;
bool inferAppIdIfOptional = true;
List<String> pickedCategories = [];
int searchnum = 0;
int urlInputKey = 0;
SourceProvider sourceProvider = SourceProvider();
linkFn(String input) {
try {
if (input.isEmpty) {
throw UnsupportedURLError();
}
sourceProvider.getSource(input);
changeUserInput(input, true, false, updateUrlInput: true);
} catch (e) {
showError(e, context);
}
}
changeUserInput(String input, bool valid, bool isBuilding,
{bool updateUrlInput = false}) {
userInput = input;
if (!isBuilding) {
setState(() {
if (updateUrlInput) {
urlInputKey++;
}
var prevHost = pickedSource?.host;
try {
var naturalSource =
valid ? sourceProvider.getSource(userInput) : null;
if (naturalSource != null &&
naturalSource.runtimeType.toString() !=
HTML().runtimeType.toString()) {
// If input has changed to match a regular source, reset the override
pickedSourceOverride = null;
}
} catch (e) {
// ignore
}
var source = valid
? sourceProvider.getSource(userInput,
overrideSource: pickedSourceOverride)
: null;
if (pickedSource.runtimeType != source.runtimeType ||
(prevHost != null && prevHost != source?.host)) {
pickedSource = source;
additionalSettings = source != null
? getDefaultValuesFromFormItems(
source.combinedAppSpecificSettingFormItems)
: {};
additionalSettingsValid = source != null
? !sourceProvider.ifRequiredAppSpecificSettingsExist(source)
: true;
inferAppIdIfOptional = true;
}
});
}
}
@override
Widget build(BuildContext context) {
AppsProvider appsProvider = context.read<AppsProvider>();
@@ -48,47 +101,6 @@ class _AddAppPageState extends State<AddAppPage> {
bool doingSomething = gettingAppInfo || searching;
changeUserInput(String input, bool valid, bool isBuilding,
{bool isSearch = false}) {
userInput = input;
if (!isBuilding) {
setState(() {
if (isSearch) {
searchnum++;
}
var prevHost = pickedSource?.host;
try {
var naturalSource =
valid ? sourceProvider.getSource(userInput) : null;
if (naturalSource != null &&
naturalSource.runtimeType.toString() !=
HTML().runtimeType.toString()) {
// If input has changed to match a regular source, reset the override
pickedSourceOverride = null;
}
} catch (e) {
// ignore
}
var source = valid
? sourceProvider.getSource(userInput,
overrideSource: pickedSourceOverride)
: null;
if (pickedSource.runtimeType != source.runtimeType ||
(prevHost != null && prevHost != source?.host)) {
pickedSource = source;
additionalSettings = source != null
? getDefaultValuesFromFormItems(
source.combinedAppSpecificSettingFormItems)
: {};
additionalSettingsValid = source != null
? !sourceProvider.ifRequiredAppSpecificSettingsExist(source)
: true;
inferAppIdIfOptional = true;
}
});
}
}
Future<bool> getTrackOnlyConfirmationIfNeeded(bool userPickedTrackOnly,
{bool ignoreHideSetting = false}) async {
var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly;
@@ -205,7 +217,7 @@ class _AddAppPageState extends State<AddAppPage> {
children: [
Expanded(
child: GeneratedForm(
key: Key(searchnum.toString()),
key: Key(urlInputKey.toString()),
items: [
[
GeneratedFormTextField('appSourceURL',
@@ -325,7 +337,7 @@ class _AddAppPageState extends State<AddAppPage> {
);
});
if (selectedUrls != null && selectedUrls.isNotEmpty) {
changeUserInput(selectedUrls[0], true, false, isSearch: true);
changeUserInput(selectedUrls[0], true, false, updateUrlInput: true);
}
}
} catch (e) {

View File

@@ -145,6 +145,29 @@ class _AppPageState extends State<AppPage> {
appsProvider.saveApps([app.app]);
}
}),
if (app?.app.additionalSettings['about'] is String &&
app?.app.additionalSettings['about'].isNotEmpty)
Column(
children: [
const SizedBox(
height: 48,
),
GestureDetector(
onLongPress: () {
Clipboard.setData(ClipboardData(
text: app?.app.additionalSettings['about'] ?? ''));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(tr('copiedToClipboard')),
));
},
child: Text(
app?.app.additionalSettings['about'],
textAlign: TextAlign.center,
style: const TextStyle(fontStyle: FontStyle.italic),
),
)
],
),
],
);

View File

@@ -496,14 +496,8 @@ class AppsPageState extends State<AppsPage> {
var transparent =
Theme.of(context).colorScheme.background.withAlpha(0).value;
List<double> stops = [
...listedApps[index]
.app
.categories
.asMap()
.entries
.map((e) =>
((e.key / (listedApps[index].app.categories.length - 1))))
,
...listedApps[index].app.categories.asMap().entries.map(
(e) => ((e.key / (listedApps[index].app.categories.length - 1)))),
1
];
if (stops.length == 2) {
@@ -516,13 +510,9 @@ class AppsPageState extends State<AppsPage> {
begin: const Alignment(-1, 0),
end: const Alignment(-0.97, 0),
colors: [
...listedApps[index]
.app
.categories
.map((e) =>
Color(settingsProvider.categories[e] ?? transparent)
.withAlpha(255))
,
...listedApps[index].app.categories.map((e) =>
Color(settingsProvider.categories[e] ?? transparent)
.withAlpha(255)),
Color(transparent)
])),
child: ListTile(
@@ -881,7 +871,7 @@ class AppsPageState extends State<AppsPage> {
onPressed: () {
String urls = '';
for (var a in selectedApps) {
urls += '${a.url}\n';
urls += 'obtainium://add/${a.url}\n';
}
urls = urls.substring(0, urls.length - 1);
Share.share(urls,
@@ -981,10 +971,8 @@ class AppsPageState extends State<AppsPage> {
defaultValue: filter.sourceFilter,
[
MapEntry('', tr('none')),
...sourceProvider.sources
.map((e) =>
MapEntry(e.runtimeType.toString(), e.name))
...sourceProvider.sources.map(
(e) => MapEntry(e.runtimeType.toString(), e.name))
])
]
],

View File

@@ -1,7 +1,11 @@
import 'dart:async';
import 'package:animations/animations.dart';
import 'package:app_links/app_links.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/pages/add_app.dart';
import 'package:obtainium/pages/apps.dart';
import 'package:obtainium/pages/import_export.dart';
@@ -30,58 +34,119 @@ class _HomePageState extends State<HomePage> {
bool isReversing = false;
int prevAppCount = -1;
bool prevIsLoading = true;
late AppLinks _appLinks;
StreamSubscription<Uri>? _linkSubscription;
bool isLinkActivity = false;
List<NavigationPageItem> pages = [
NavigationPageItem(tr('appsString'), Icons.apps,
AppsPage(key: GlobalKey<AppsPageState>())),
NavigationPageItem(tr('addApp'), Icons.add, const AddAppPage()),
NavigationPageItem(
tr('addApp'), Icons.add, AddAppPage(key: GlobalKey<AddAppPageState>())),
NavigationPageItem(
tr('importExport'), Icons.import_export, const ImportExportPage()),
NavigationPageItem(tr('settings'), Icons.settings, const SettingsPage())
];
@override
void initState() {
super.initState();
initDeepLinks();
}
Future<void> initDeepLinks() async {
_appLinks = AppLinks();
goToAddApp(String data) async {
switchToPage(1);
while (
(pages[1].widget.key as GlobalKey<AddAppPageState>?)?.currentState ==
null) {
await Future.delayed(const Duration(microseconds: 1));
}
(pages[1].widget.key as GlobalKey<AddAppPageState>?)
?.currentState
?.linkFn(data);
}
interpretLink(Uri uri) async {
isLinkActivity = true;
var action = uri.host;
var data = uri.path.length > 1 ? uri.path.substring(1) : "";
try {
if (action == 'add') {
await goToAddApp(data);
} else if (action == 'app') {
await context
.read<AppsProvider>()
.import('{ "apps": [${Uri.decodeComponent(data)}] }');
} else if (action == 'apps') {
await context
.read<AppsProvider>()
.import('{ "apps": ${Uri.decodeComponent(data)} }');
} else {
throw ObtainiumError(tr('unknown'));
}
} catch (e) {
showError(e, context);
}
}
// Check initial link if app was in cold state (terminated)
final appLink = await _appLinks.getInitialAppLink();
if (appLink != null) {
await interpretLink(appLink);
}
// Handle link when app is in warm state (front or background)
_linkSubscription = _appLinks.uriLinkStream.listen((uri) async {
await interpretLink(uri);
});
}
setIsReversing(int targetIndex) {
bool reversing = selectedIndexHistory.isNotEmpty &&
selectedIndexHistory.last > targetIndex;
setState(() {
isReversing = reversing;
});
}
switchToPage(int index) async {
setIsReversing(index);
if (index == 0) {
while ((pages[0].widget.key as GlobalKey<AppsPageState>).currentState !=
null) {
// Avoid duplicate GlobalKey error
await Future.delayed(const Duration(microseconds: 1));
}
setState(() {
selectedIndexHistory.clear();
});
} else if (selectedIndexHistory.isEmpty ||
(selectedIndexHistory.isNotEmpty &&
selectedIndexHistory.last != index)) {
setState(() {
int existingInd = selectedIndexHistory.indexOf(index);
if (existingInd >= 0) {
selectedIndexHistory.removeAt(existingInd);
}
selectedIndexHistory.add(index);
});
}
}
@override
Widget build(BuildContext context) {
AppsProvider appsProvider = context.watch<AppsProvider>();
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
setIsReversing(int targetIndex) {
bool reversing = selectedIndexHistory.isNotEmpty &&
selectedIndexHistory.last > targetIndex;
setState(() {
isReversing = reversing;
});
}
switchToPage(int index) async {
setIsReversing(index);
if (index == 0) {
while ((pages[0].widget.key as GlobalKey<AppsPageState>).currentState !=
null) {
// Avoid duplicate GlobalKey error
await Future.delayed(const Duration(microseconds: 1));
}
setState(() {
selectedIndexHistory.clear();
});
} else if (selectedIndexHistory.isEmpty ||
(selectedIndexHistory.isNotEmpty &&
selectedIndexHistory.last != index)) {
setState(() {
int existingInd = selectedIndexHistory.indexOf(index);
if (existingInd >= 0) {
selectedIndexHistory.removeAt(existingInd);
}
selectedIndexHistory.add(index);
});
}
}
if (!prevIsLoading &&
prevAppCount >= 0 &&
appsProvider.apps.length > prevAppCount &&
selectedIndexHistory.isNotEmpty &&
selectedIndexHistory.last == 1) {
selectedIndexHistory.last == 1 &&
!isLinkActivity) {
switchToPage(0);
}
prevAppCount = appsProvider.apps.length;
@@ -129,6 +194,11 @@ class _HomePageState extends State<HomePage> {
),
),
onWillPop: () async {
if (isLinkActivity &&
selectedIndexHistory.length == 1 &&
selectedIndexHistory.last == 1) {
return true;
}
setIsReversing(selectedIndexHistory.length >= 2
? selectedIndexHistory.reversed.toList()[1]
: 0);
@@ -143,4 +213,10 @@ class _HomePageState extends State<HomePage> {
?.clearSelected();
});
}
@override
void dispose() {
super.dispose();
_linkSubscription?.cancel();
}
}

View File

@@ -106,7 +106,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
runObtainiumExport({bool pickOnly = false}) async {
HapticFeedback.selectionClick();
appsProvider
.exportApps(
.export(
pickOnly:
pickOnly || (await settingsProvider.getExportDir()) == null,
sp: settingsProvider)
@@ -132,7 +132,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
} catch (e) {
throw ObtainiumError(tr('invalidInput'));
}
appsProvider.importApps(data).then((value) {
appsProvider.import(data).then((value) {
var cats = settingsProvider.categories;
appsProvider.apps.forEach((key, value) {
for (var c in value.app.categories) {
@@ -143,7 +143,10 @@ class _ImportExportPageState extends State<ImportExportPage> {
});
appsProvider.addMissingCategories(settingsProvider);
showMessage(
tr('importedX', args: [plural('apps', value)]), context);
'${tr('importedX', args: [
plural('apps', value.key)
])}${value.value ? ' + ${tr('settings')}' : ''}',
context);
});
} else {
// User canceled the picker
@@ -344,7 +347,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
: () {
runObtainiumExport(pickOnly: true);
},
child: Text(tr('pickExportDir')),
child: Text(tr('pickExportDir'),
textAlign: TextAlign.center),
)),
const SizedBox(
width: 16,
@@ -357,7 +361,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
snapshot.data == null
? null
: runObtainiumExport,
child: Text(tr('obtainiumExport')),
child: Text(tr('obtainiumExport'),
textAlign: TextAlign.center),
)),
],
),
@@ -372,7 +377,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
onPressed: importInProgress
? null
: runObtainiumImport,
child: Text(tr('obtainiumImport')))),
child: Text(tr('obtainiumImport'),
textAlign: TextAlign.center))),
],
),
if (snapshot.data != null)
@@ -388,6 +394,14 @@ class _ImportExportPageState extends State<ImportExportPage> {
defaultValue: settingsProvider
.autoExportOnChanges,
)
],
[
GeneratedFormSwitch(
'exportSettings',
label: tr('includeSettings'),
defaultValue: settingsProvider
.exportSettings,
)
]
],
onValueChanges:
@@ -400,6 +414,12 @@ class _ImportExportPageState extends State<ImportExportPage> {
'autoExportOnChanges'] ==
true;
}
if (value['exportSettings'] !=
null) {
settingsProvider.exportSettings =
value['exportSettings'] ==
true;
}
}
}),
],
@@ -598,6 +618,7 @@ class SelectionModal extends StatefulWidget {
class _SelectionModalState extends State<SelectionModal> {
Map<MapEntry<String, List<String>>, bool> entrySelections = {};
String filterRegex = '';
@override
void initState() {
super.initState();
@@ -618,11 +639,50 @@ class _SelectionModalState extends State<SelectionModal> {
@override
Widget build(BuildContext context) {
Map<MapEntry<String, List<String>>, bool> filteredEntrySelections = {};
entrySelections.forEach((key, value) {
var searchableText = key.value.isEmpty ? key.key : key.value[0];
if (filterRegex.isEmpty || RegExp(filterRegex).hasMatch(searchableText)) {
filteredEntrySelections.putIfAbsent(key, () => value);
}
});
if (filterRegex.isNotEmpty && filteredEntrySelections.isEmpty) {
entrySelections.forEach((key, value) {
var searchableText = key.value.isEmpty ? key.key : key.value[0];
if (filterRegex.isEmpty ||
RegExp(filterRegex, caseSensitive: false)
.hasMatch(searchableText)) {
filteredEntrySelections.putIfAbsent(key, () => value);
}
});
}
return AlertDialog(
scrollable: true,
title: Text(widget.title ?? tr('pick')),
content: Column(children: [
...entrySelections.keys.map((entry) {
GeneratedForm(
items: [
[
GeneratedFormTextField('filter',
label: tr('filter'),
required: false,
additionalValidators: [
(value) {
return regExValidator(value);
}
])
]
],
onValueChanges: (value, valid, isBuilding) {
if (valid && !isBuilding) {
if (value['filter'] != null) {
setState(() {
filterRegex = value['filter'];
});
}
}
}),
...filteredEntrySelections.keys.map((entry) {
selectThis(bool? value) {
setState(() {
value ??= false;

View File

@@ -327,6 +327,19 @@ class _SettingsPageState extends State<SettingsPage> {
})
],
),
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(tr('parallelDownloads'))),
Switch(
value: settingsProvider.parallelDownloads,
onChanged: (value) {
settingsProvider.parallelDownloads =
value;
})
],
),
height32,
Text(
tr('sourceSpecific'),

View File

@@ -657,7 +657,7 @@ class AppsProvider with ChangeNotifier {
appsToInstall =
moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId);
for (var id in appsToInstall) {
Future<void> updateFn(String id, {bool skipInstalls = false}) async {
try {
var downloadedArtifact =
// ignore: use_build_context_synchronously
@@ -682,24 +682,26 @@ class AppsProvider with ChangeNotifier {
apps[id]?.downloadProgress = -1;
notifyListeners();
try {
if (downloadedFile != null) {
if (willBeSilent && context == null) {
installApk(downloadedFile, needsBGWorkaround: true);
if (!skipInstalls) {
if (downloadedFile != null) {
if (willBeSilent && context == null) {
installApk(downloadedFile, needsBGWorkaround: true);
} else {
await installApk(downloadedFile);
}
} else {
await installApk(downloadedFile);
if (willBeSilent && context == null) {
installXApkDir(downloadedDir!, needsBGWorkaround: true);
} else {
await installXApkDir(downloadedDir!);
}
}
} else {
if (willBeSilent && context == null) {
installXApkDir(downloadedDir!, needsBGWorkaround: true);
} else {
await installXApkDir(downloadedDir!);
notificationsProvider?.notify(SilentUpdateAttemptNotification(
[apps[appId]!.app],
id: appId.hashCode));
}
}
if (willBeSilent && context == null) {
notificationsProvider?.notify(SilentUpdateAttemptNotification(
[apps[appId]!.app],
id: appId.hashCode));
}
} finally {
apps[id]?.downloadProgress = null;
notifyListeners();
@@ -710,6 +712,18 @@ class AppsProvider with ChangeNotifier {
}
}
if (!settingsProvider.parallelDownloads) {
for (var id in appsToInstall) {
await updateFn(id);
}
} else {
await Future.wait(
appsToInstall.map((id) => updateFn(id, skipInstalls: true)));
for (var id in appsToInstall) {
await updateFn(id);
}
}
if (errors.idsByErrorString.isNotEmpty) {
throw errors;
}
@@ -726,12 +740,15 @@ class AppsProvider with ChangeNotifier {
return appsDir;
}
Future<PackageInfo?> getInstalledInfo(String? packageName) async {
Future<PackageInfo?> getInstalledInfo(String? packageName,
{bool printErr = true}) async {
if (packageName != null) {
try {
return await pm.getPackageInfo(packageName: packageName);
} catch (e) {
print(e); // OK
if (printErr) {
print(e); // OK
}
}
}
return null;
@@ -974,7 +991,7 @@ class AppsProvider with ChangeNotifier {
}
}
notifyListeners();
exportApps(isAuto: true);
export(isAuto: true);
}
Future<void> removeApps(List<String> appIds) async {
@@ -996,7 +1013,7 @@ class AppsProvider with ChangeNotifier {
}
if (appIds.isNotEmpty) {
notifyListeners();
exportApps(isAuto: true);
export(isAuto: true);
}
}
@@ -1173,7 +1190,7 @@ class AppsProvider with ChangeNotifier {
return updateAppIds;
}
Future<String?> exportApps(
Future<String?> export(
{bool pickOnly = false, isAuto = false, SettingsProvider? sp}) async {
SettingsProvider settingsProvider = sp ?? this.settingsProvider;
var exportDir = await settingsProvider.getExportDir();
@@ -1203,12 +1220,22 @@ class AppsProvider with ChangeNotifier {
}
String? returnPath;
if (!pickOnly) {
Map<String, dynamic> finalExport = {};
finalExport['apps'] = apps.values.map((e) => e.app.toJson()).toList();
if (settingsProvider.exportSettings) {
finalExport['settings'] = Map<String, Object?>.fromEntries(
(settingsProvider.prefs
?.getKeys()
.map((key) =>
MapEntry(key, settingsProvider.prefs?.get(key)))
.toList()) ??
[]);
}
var result = await saf.createFile(exportDir,
displayName:
'${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().toIso8601String().replaceAll(':', '-')}${isAuto ? '-auto' : ''}.json',
mimeType: 'application/json',
bytes: Uint8List.fromList(utf8.encode(
jsonEncode(apps.values.map((e) => e.app.toJson()).toList()))));
bytes: Uint8List.fromList(utf8.encode(jsonEncode(finalExport))));
if (result == null) {
throw ObtainiumError(tr('unexpectedError'));
}
@@ -1218,21 +1245,36 @@ class AppsProvider with ChangeNotifier {
return returnPath;
}
Future<int> importApps(String appsJSON) async {
List<App> importedApps = (jsonDecode(appsJSON) as List<dynamic>)
.map((e) => App.fromJson(e))
.toList();
Future<MapEntry<int, bool>> import(String appsJSON) async {
var decodedJSON = jsonDecode(appsJSON);
var newFormat = decodedJSON is! List;
List<App> importedApps =
((newFormat ? decodedJSON['apps'] : decodedJSON) as List<dynamic>)
.map((e) => App.fromJson(e))
.toList();
while (loadingApps) {
await Future.delayed(const Duration(microseconds: 1));
}
for (App a in importedApps) {
if (apps[a.id]?.app.installedVersion != null) {
a.installedVersion = apps[a.id]?.app.installedVersion;
}
a.installedVersion =
(await getInstalledInfo(a.id, printErr: false))?.versionName;
}
await saveApps(importedApps, onlyIfExists: false);
notifyListeners();
return importedApps.length;
if (newFormat && decodedJSON['settings'] != null) {
var settingsMap = decodedJSON['settings'] as Map<String, Object?>;
settingsMap.forEach((key, value) {
if (value is int) {
settingsProvider.prefs?.setInt(key, value);
} else if (value is bool) {
settingsProvider.prefs?.setBool(key, value);
} else {
settingsProvider.prefs?.setString(key, value as String);
}
});
}
return MapEntry<int, bool>(
importedApps.length, newFormat && decodedJSON['settings'] != null);
}
@override

View File

@@ -213,7 +213,8 @@ class SettingsProvider with ChangeNotifier {
}
String? getSettingString(String settingId) {
return prefs?.getString(settingId);
String? str = prefs?.getString(settingId);
return str?.isNotEmpty == true ? str : null;
}
void setSettingString(String settingId, String value) {
@@ -415,4 +416,22 @@ class SettingsProvider with ChangeNotifier {
prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val);
notifyListeners();
}
bool get exportSettings {
return prefs?.getBool('exportSettings') ?? false;
}
set exportSettings(bool val) {
prefs?.setBool('exportSettings', val);
notifyListeners();
}
bool get parallelDownloads {
return prefs?.getBool('parallelDownloads') ?? false;
}
set parallelDownloads(bool val) {
prefs?.setBool('parallelDownloads', val);
notifyListeners();
}
}

View File

@@ -454,7 +454,8 @@ abstract class AppSource {
[
GeneratedFormSwitch('skipUpdateNotifications',
label: tr('skipUpdateNotifications'))
]
],
[GeneratedFormTextField('about', label: tr('about'), required: false)]
];
// Previous 2 variables combined into one at runtime for convenient usage
@@ -683,8 +684,9 @@ class SourceProvider {
name = name.isNotEmpty ? name : apk.names.name;
App finalApp = App(
currentApp?.id ??
((!source.appIdInferIsOptional ||
(source.appIdInferIsOptional && inferAppIdIfOptional))
(!trackOnly &&
(!source.appIdInferIsOptional ||
(source.appIdInferIsOptional && inferAppIdIfOptional))
? await source.tryInferringAppId(standardUrl,
additionalSettings: additionalSettings)
: null) ??
@@ -704,8 +706,9 @@ class SourceProvider {
changeLog: apk.changeLog,
overrideSource: overrideSource ?? currentApp?.overrideSource,
allowIdChange: currentApp?.allowIdChange ??
source.appIdInferIsOptional &&
inferAppIdIfOptional // Optional ID inferring may be incorrect - allow correction on first install
trackOnly ||
(source.appIdInferIsOptional &&
inferAppIdIfOptional) // Optional ID inferring may be incorrect - allow correction on first install
);
return source.endOfGetAppChanges(finalApp);
}

View File

@@ -5,10 +5,10 @@ packages:
dependency: "direct main"
description:
name: android_alarm_manager_plus
sha256: "82fb28c867c4b3dd7e9157728e46426b8916362f977dbba46b949210f00099f4"
sha256: "84720c8ad2758aabfbeafd24a8c355d8c8dd3aa52b01eaf3bb827c7210f61a91"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.4"
android_intent_plus:
dependency: "direct main"
description:
@@ -38,10 +38,18 @@ packages:
dependency: "direct main"
description:
name: animations
sha256: ef57563eed3620bd5d75ad96189846aca1e033c0c45fc9a7d26e80ab02b88a70
sha256: "708e4b68c23228c264b038fe7003a2f5d01ce85fc64d8cae090e86b27fcea6c5"
url: "https://pub.dev"
source: hosted
version: "2.0.8"
version: "2.0.10"
app_links:
dependency: "direct main"
description:
name: app_links
sha256: "4e392b5eba997df356ca6021f28431ce1cfeb16758699553a94b13add874a3bb"
url: "https://pub.dev"
source: hosted
version: "3.5.0"
archive:
dependency: transitive
description:
@@ -94,10 +102,10 @@ packages:
dependency: transitive
description:
name: cli_util
sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev"
source: hosted
version: "0.4.0"
version: "0.4.1"
clock:
dependency: transitive
description:
@@ -142,10 +150,10 @@ packages:
dependency: transitive
description:
name: cross_file
sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5"
sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
url: "https://pub.dev"
source: hosted
version: "0.3.3+7"
version: "0.3.3+8"
crypto:
dependency: "direct main"
description:
@@ -198,10 +206,10 @@ packages:
dependency: "direct main"
description:
name: dynamic_color
sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f"
sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b
url: "https://pub.dev"
source: hosted
version: "1.6.8"
version: "1.6.9"
easy_localization:
dependency: "direct main"
description:
@@ -259,10 +267,10 @@ packages:
dependency: "direct main"
description:
name: flutter_archive
sha256: aec85d1da65e5b33a529db00a86df0b8e92bda78088a7cfaeeba5187701d0d85
sha256: "004132780d382df5171589ab793e2efc9c3eef570fe72d78b4ccfbfbe52762ae"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
version: "6.0.0"
flutter_fgbg:
dependency: "direct main"
description:
@@ -350,6 +358,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.2.4"
gtk:
dependency: transitive
description:
name: gtk
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
url: "https://pub.dev"
source: hosted
version: "2.1.0"
hsluv:
dependency: "direct main"
description:
@@ -370,10 +386,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.2"
http_parser:
dependency: transitive
description:
@@ -562,10 +578,10 @@ packages:
dependency: transitive
description:
name: permission_handler_html
sha256: d96ff56a757b7f04fa825c469d296c5aebc55f743e87bd639fef91a466a24da8
sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df"
url: "https://pub.dev"
source: hosted
version: "0.1.0+1"
version: "0.1.0+2"
permission_handler_platform_interface:
dependency: transitive
description:
@@ -586,10 +602,10 @@ packages:
dependency: transitive
description:
name: petitparser
sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "6.0.1"
version: "6.0.2"
platform:
dependency: transitive
description:
@@ -767,10 +783,10 @@ packages:
dependency: transitive
description:
name: synchronized
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.0+1"
term_glyph:
dependency: transitive
description:
@@ -807,10 +823,10 @@ packages:
dependency: "direct main"
description:
name: url_launcher
sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86
url: "https://pub.dev"
source: hosted
version: "6.2.1"
version: "6.2.2"
url_launcher_android:
dependency: transitive
description:
@@ -831,10 +847,10 @@ packages:
dependency: transitive
description:
name: url_launcher_linux
sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.1"
url_launcher_macos:
dependency: transitive
description:
@@ -855,26 +871,26 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7"
sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.2.2"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.1"
uuid:
dependency: transitive
description:
name: uuid
sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921
sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
url: "https://pub.dev"
source: hosted
version: "4.2.1"
version: "4.2.2"
vector_math:
dependency: transitive
description:
@@ -903,34 +919,34 @@ packages:
dependency: transitive
description:
name: webview_flutter_android
sha256: "8326ee235f87605a2bfc444a4abc897f4abc78d83f054ba7d3d1074ce82b4fbf"
sha256: b54c89fe14a6d26a2a46e24880da0441cdd2bf1f6d01a5b3e1d39558feb1de0b
url: "https://pub.dev"
source: hosted
version: "3.12.1"
version: "3.13.1"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: adb8c03c2be231bea5a8ed0e9039e9d18dbb049603376beaefa15393ede468a5
sha256: dbe745ee459a16b6fec296f7565a8ef430d0d681001d8ae521898b9361854943
url: "https://pub.dev"
source: hosted
version: "2.7.0"
version: "2.9.0"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: accdaaa49a2aca2dc3c3230907988954cdd23fed0a19525d6c9789d380f4dc76
sha256: eebfabfa8a115b535b52031b8b26f7a4b58ceceab378bc9db8762b0fb46f7b5d
url: "https://pub.dev"
source: hosted
version: "3.9.4"
version: "3.10.0"
win32:
dependency: transitive
description:
name: win32
sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f"
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
url: "https://pub.dev"
source: hosted
version: "5.1.0"
version: "5.1.1"
win32_registry:
dependency: transitive
description:
@@ -951,10 +967,10 @@ packages:
dependency: transitive
description:
name: xml
sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "6.4.2"
version: "6.5.0"
yaml:
dependency: transitive
description:

View File

@@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# 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: 0.14.35+229 # When changing this, update the tag in main() accordingly
version: 0.14.40+234 # When changing this, update the tag in main() accordingly
environment:
sdk: '>=3.0.0 <4.0.0'
@@ -62,11 +62,12 @@ dependencies:
easy_localization: ^3.0.1
android_intent_plus: ^4.0.0
flutter_markdown: ^0.6.14
flutter_archive: ^5.0.0
flutter_archive: ^6.0.0
hsluv: ^1.1.3
connectivity_plus: ^5.0.0
shared_storage: ^0.8.0
crypto: ^3.0.3
app_links: ^3.5.0
dev_dependencies:
flutter_test: