Compare commits

...

39 Commits

Author SHA1 Message Date
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
34 changed files with 664 additions and 401 deletions

View File

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

View File

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

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku", "highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
"pickExportDir": "Vybrat adresář pro export", "pickExportDir": "Vybrat adresář pro export",
"autoExportOnChanges": "Automatický export při změnách", "autoExportOnChanges": "Automatický export při změnách",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu", "filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu",
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK", "trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
"dontSortReleasesList": "Retain release order from API", "dontSortReleasesList": "Retain release order from API",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben", "highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben",
"pickExportDir": "Export-Verzeichnis wählen", "pickExportDir": "Export-Verzeichnis wählen",
"autoExportOnChanges": "Automatischer Export bei Änderung(en)", "autoExportOnChanges": "Automatischer Export bei Änderung(en)",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern", "filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
"trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen", "trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
"dontSortReleasesList": "Freigaberelease von der API ordern", "dontSortReleasesList": "Freigaberelease von der API ordern",
@@ -275,8 +276,8 @@
"completeAppInstallationNotifChannel": "App Installation abschließen", "completeAppInstallationNotifChannel": "App Installation abschließen",
"checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen", "checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen",
"onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen", "onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen",
"supportFixedAPKURL": "Support fixed APK URLs", "supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt",
"selectX": "Select {}", "selectX": "Wähle {}",
"removeAppQuestion": { "removeAppQuestion": {
"one": "App entfernen?", "one": "App entfernen?",
"other": "Apps entfernen?" "other": "Apps entfernen?"
@@ -329,4 +330,4 @@
"one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.", "one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
"other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert." "other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
} }
} }

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Highlight less obvious touch targets", "highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory", "pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes", "autoExportOnChanges": "Auto-export on changes",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API", "dontSortReleasesList": "Retain release order from API",

View File

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

View File

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

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Highlight less obvious touch targets", "highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory", "pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes", "autoExportOnChanges": "Auto-export on changes",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API", "dontSortReleasesList": "Retain release order from API",

View File

@@ -215,7 +215,7 @@
"versionDetection": "Verzió érzékelés", "versionDetection": "Verzió érzékelés",
"standardVersionDetection": "Alapért. verzió érzékelés", "standardVersionDetection": "Alapért. verzió érzékelés",
"groupByCategory": "Csoportosítás Kategória alapján", "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", "overrideSource": "Forrás felülbírálása",
"dontShowAgain": "Ne mutassa ezt újra", "dontShowAgain": "Ne mutassa ezt újra",
"dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést", "dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
@@ -255,6 +255,7 @@
"highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat", "highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
"pickExportDir": "Válassza az Exportálási könyvtárat", "pickExportDir": "Válassza az Exportálási könyvtárat",
"autoExportOnChanges": "Auto-exportálás a változások után", "autoExportOnChanges": "Auto-exportálás a változások után",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel", "filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t", "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", "dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
@@ -275,8 +276,8 @@
"completeAppInstallationNotifChannel": "Teljes app telepítés", "completeAppInstallationNotifChannel": "Teljes app telepítés",
"checkingForUpdatesNotifChannel": "Frissítések keresése", "checkingForUpdatesNotifChannel": "Frissítések keresése",
"onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak követhető appokat ellenőrizze frissítésekért", "onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak követhető appokat ellenőrizze frissítésekért",
"supportFixedAPKURL": "Support fixed APK URLs", "supportFixedAPKURL": "Támogatja a rögzített APK URL-eket",
"selectX": "Select {}", "selectX": "Kiválaszt {}",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Eltávolítja az alkalmazást?", "one": "Eltávolítja az alkalmazást?",
"other": "Eltávolítja az alkalmazást?" "other": "Eltávolítja az alkalmazást?"

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi", "highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi",
"pickExportDir": "Scegli cartella esp.", "pickExportDir": "Scegli cartella esp.",
"autoExportOnChanges": "Auto-esporta dopo modifiche", "autoExportOnChanges": "Auto-esporta dopo modifiche",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtra versioni con espressione regolare", "filterVersionsByRegEx": "Filtra versioni con espressione regolare",
"trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito", "trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito",
"dontSortReleasesList": "Conserva l'ordine di release da API", "dontSortReleasesList": "Conserva l'ordine di release da API",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "目立たないタップ可能な対象をハイライトする", "highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
"pickExportDir": "エクスポートディレクトリを選択", "pickExportDir": "エクスポートディレクトリを選択",
"autoExportOnChanges": "変更があった際に自動でエクスポートする", "autoExportOnChanges": "変更があった際に自動でエクスポートする",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする", "filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する", "trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
"dontSortReleasesList": "APIからのリリース順を保持する", "dontSortReleasesList": "APIからのリリース順を保持する",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.", "highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.",
"pickExportDir": "Kies de exportmap", "pickExportDir": "Kies de exportmap",
"autoExportOnChanges": "Automatisch exporteren bij wijzigingen", "autoExportOnChanges": "Automatisch exporteren bij wijzigingen",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filter versies met een reguliere expressie", "filterVersionsByRegEx": "Filter versies met een reguliere expressie",
"trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren", "trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
"dontSortReleasesList": "Volgorde van releases behouden vanuit de API", "dontSortReleasesList": "Volgorde van releases behouden vanuit de API",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe", "highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
"pickExportDir": "Wybierz katalog eksportu", "pickExportDir": "Wybierz katalog eksportu",
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian", "autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego", "filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK", "trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
"dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API", "dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
@@ -275,8 +276,8 @@
"completeAppInstallationNotifChannel": "Ukończenie instalacji aplikacji", "completeAppInstallationNotifChannel": "Ukończenie instalacji aplikacji",
"checkingForUpdatesNotifChannel": "Sprawdzanie dostępności aktualizacji", "checkingForUpdatesNotifChannel": "Sprawdzanie dostępności aktualizacji",
"onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji", "onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
"supportFixedAPKURL": "Support fixed APK URLs", "supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
"selectX": "Select {}", "selectX": "Wybierz {}",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Usunąć aplikację?", "one": "Usunąć aplikację?",
"few": "Usunąć aplikacje?", "few": "Usunąć aplikacje?",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Destaque areas de toque menos óbvias", "highlightTouchTargets": "Destaque areas de toque menos óbvias",
"pickExportDir": "Escolher Diretorio de Exportação", "pickExportDir": "Escolher Diretorio de Exportação",
"autoExportOnChanges": "Auto-exportar em mudanças", "autoExportOnChanges": "Auto-exportar em mudanças",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filtrar Versões por Expressão Regular", "filterVersionsByRegEx": "Filtrar Versões por Expressão Regular",
"trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida", "trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
"dontSortReleasesList": "Reter a ordem de lançamento da API", "dontSortReleasesList": "Reter a ordem de lançamento da API",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием", "highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
"pickExportDir": "Выбрать каталог для экспорта", "pickExportDir": "Выбрать каталог для экспорта",
"autoExportOnChanges": "Автоэкспорт при изменениях", "autoExportOnChanges": "Автоэкспорт при изменениях",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению", "filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK", "trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
"dontSortReleasesList": "Сохранить порядок релизов от API", "dontSortReleasesList": "Сохранить порядок релизов от API",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Highlight less obvious touch targets", "highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Välj Exportsökväg", "pickExportDir": "Välj Exportsökväg",
"autoExportOnChanges": "Automatisk export vid ändringar", "autoExportOnChanges": "Automatisk export vid ändringar",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API", "dontSortReleasesList": "Retain release order from API",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula", "highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula",
"pickExportDir": "Dışa Aktarılacak Klasörü Seç", "pickExportDir": "Dışa Aktarılacak Klasörü Seç",
"autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar", "autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele", "filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele",
"trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene", "trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
"dontSortReleasesList": "API'den sıralama düzenini koru", "dontSortReleasesList": "API'den sıralama düzenini koru",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn", "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", "pickExportDir": "Chọn thư mục xuất",
"autoExportOnChanges": "Tự động xuất khi thay đổi", "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", "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", "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", "dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",

View File

@@ -256,6 +256,7 @@
"highlightTouchTargets": "突出展示不明显的触摸区域", "highlightTouchTargets": "突出展示不明显的触摸区域",
"pickExportDir": "选择导出文件夹", "pickExportDir": "选择导出文件夹",
"autoExportOnChanges": "数据变更时自动导出", "autoExportOnChanges": "数据变更时自动导出",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "筛选版本号(正则表达式)", "filterVersionsByRegEx": "筛选版本号(正则表达式)",
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件", "trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
"dontSortReleasesList": "保持来自 API 的发行顺序", "dontSortReleasesList": "保持来自 API 的发行顺序",

View File

@@ -65,7 +65,7 @@ class FDroid extends AppSource {
) async { ) async {
String? appId = await tryInferringAppId(standardUrl); String? appId = await tryInferringAppId(standardUrl);
String host = Uri.parse(standardUrl).host; String host = Uri.parse(standardUrl).host;
return getAPKUrlsFromFDroidPackagesAPIResponse( var details = getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest('https://$host/api/v1/packages/$appId'), await sourceRequest('https://$host/api/v1/packages/$appId'),
'https://$host/repo/$appId', 'https://$host/repo/$appId',
standardUrl, standardUrl,
@@ -80,6 +80,23 @@ class FDroid extends AppSource {
true true
? additionalSettings['filterVersionsByRegEx'] ? additionalSettings['filterVersionsByRegEx']
: null); : 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 @override
@@ -111,79 +128,79 @@ class FDroid extends AppSource {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }
} }
}
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse( APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
Response res, String apkUrlPrefix, String standardUrl, String sourceName, Response res, String apkUrlPrefix, String standardUrl, String sourceName,
{bool autoSelectHighestVersionCode = false, {bool autoSelectHighestVersionCode = false,
bool trySelectingSuggestedVersionCode = false, bool trySelectingSuggestedVersionCode = false,
String? filterVersionsByRegEx}) { String? filterVersionsByRegEx}) {
if (res.statusCode == 200) { if (res.statusCode == 200) {
var response = jsonDecode(res.body); var response = jsonDecode(res.body);
List<dynamic> releases = response['packages'] ?? []; List<dynamic> releases = response['packages'] ?? [];
if (releases.isEmpty) { if (releases.isEmpty) {
throw NoReleasesError(); 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'];
} }
} String? version;
// Apply the release filter if any Iterable<dynamic> releaseChoices = [];
if (filterVersionsByRegEx?.isNotEmpty == true) { // Grab the versionCode suggested if the user chose to do that
version = null; // Only do so at this stage if the user has no release filter
releaseChoices = []; if (trySelectingSuggestedVersionCode &&
for (var i = 0; i < releases.length; i++) { response['suggestedVersionCode'] != null &&
if (RegExp(filterVersionsByRegEx!) filterVersionsByRegEx == null) {
.hasMatch(releases[i]['versionName'])) { var suggestedReleases = releases.where((element) =>
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']); element['versionCode'] == response['suggestedVersionCode']);
if (suggestedReleases.isNotEmpty) { if (suggestedReleases.isNotEmpty) {
releaseChoices = suggestedReleases; 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 verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
bool dontSortReleasesList = bool dontSortReleasesList =
additionalSettings['dontSortReleasesList'] == true; additionalSettings['dontSortReleasesList'] == true;
String? latestTag; dynamic latestRelease;
if (verifyLatestTag) { if (verifyLatestTag) {
var temp = requestUrl.split('?'); var temp = requestUrl.split('?');
Response res = await sourceRequest( Response res = await sourceRequest(
@@ -245,12 +245,20 @@ class GitHub extends AppSource {
} }
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }
var jsres = jsonDecode(res.body); latestRelease = jsonDecode(res.body);
latestTag = jsres['tag_name'] ?? jsres['name'];
} }
Response res = await sourceRequest(requestUrl); Response res = await sourceRequest(requestUrl);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var releases = jsonDecode(res.body) as List<dynamic>; 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) => List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
(release['assets'] as List<dynamic>?) (release['assets'] as List<dynamic>?)
@@ -299,13 +307,13 @@ class GitHub extends AppSource {
} }
}); });
} }
if (latestTag != null && if (latestRelease != null &&
releases.isNotEmpty && releases.isNotEmpty &&
latestTag != latestRelease !=
(releases[releases.length - 1]['tag_name'] ?? (releases[releases.length - 1]['tag_name'] ??
releases[0]['name'])) { releases[0]['name'])) {
var ind = releases.indexWhere( var ind = releases.indexWhere((element) =>
(element) => latestTag == (element['tag_name'] ?? element['name'])); latestRelease == (element['tag_name'] ?? element['name']));
if (ind >= 0) { if (ind >= 0) {
releases.add(releases.removeAt(ind)); releases.add(releases.removeAt(ind));
} }

View File

@@ -48,12 +48,6 @@ class GitLab extends AppSource {
label: tr('fallbackToOlderReleases'), defaultValue: true) label: tr('fallbackToOlderReleases'), defaultValue: true)
] ]
]; ];
searchQuerySettingFormItems = [
GeneratedFormTextField('PAT',
label: tr('gitlabPATLabel').split('(')[0],
password: true,
required: false)
];
} }
@override @override
@@ -86,18 +80,8 @@ class GitLab extends AppSource {
@override @override
Future<Map<String, List<String>>> search(String query, Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async { {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 = 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); var res = await sourceRequest(url);
if (res.statusCode != 200) { if (res.statusCode != 200) {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
@@ -203,7 +187,7 @@ class GitLab extends AppSource {
}); });
} }
if (apkDetailsList.isEmpty) { if (apkDetailsList.isEmpty) {
throw NoReleasesError(); throw NoReleasesError(note: tr('gitlabSourceNote'));
} }
if (fallbackToOlderReleases) { if (fallbackToOlderReleases) {
if (additionalSettings['trackOnly'] != true) { if (additionalSettings['trackOnly'] != true) {
@@ -211,7 +195,7 @@ class GitLab extends AppSource {
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList(); apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
} }
if (apkDetailsList.isEmpty) { if (apkDetailsList.isEmpty) {
throw NoReleasesError(); throw NoReleasesError(note: tr('gitlabSourceNote'));
} }
} }
return apkDetailsList.first; return apkDetailsList.first;

View File

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

View File

@@ -34,7 +34,9 @@ class CredsNeededError extends ObtainiumError {
} }
class NoReleasesError 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 { class NoAPKError extends ObtainiumError {

View File

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

View File

@@ -21,10 +21,10 @@ class AddAppPage extends StatefulWidget {
const AddAppPage({super.key}); const AddAppPage({super.key});
@override @override
State<AddAppPage> createState() => _AddAppPageState(); State<AddAppPage> createState() => AddAppPageState();
} }
class _AddAppPageState extends State<AddAppPage> { class AddAppPageState extends State<AddAppPage> {
bool gettingAppInfo = false; bool gettingAppInfo = false;
bool searching = false; bool searching = false;
@@ -36,9 +36,62 @@ class _AddAppPageState extends State<AddAppPage> {
bool additionalSettingsValid = true; bool additionalSettingsValid = true;
bool inferAppIdIfOptional = true; bool inferAppIdIfOptional = true;
List<String> pickedCategories = []; List<String> pickedCategories = [];
int searchnum = 0; int urlInputKey = 0;
SourceProvider sourceProvider = SourceProvider(); 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
AppsProvider appsProvider = context.read<AppsProvider>(); AppsProvider appsProvider = context.read<AppsProvider>();
@@ -48,47 +101,6 @@ class _AddAppPageState extends State<AddAppPage> {
bool doingSomething = gettingAppInfo || searching; 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, Future<bool> getTrackOnlyConfirmationIfNeeded(bool userPickedTrackOnly,
{bool ignoreHideSetting = false}) async { {bool ignoreHideSetting = false}) async {
var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly; var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly;
@@ -205,7 +217,7 @@ class _AddAppPageState extends State<AddAppPage> {
children: [ children: [
Expanded( Expanded(
child: GeneratedForm( child: GeneratedForm(
key: Key(searchnum.toString()), key: Key(urlInputKey.toString()),
items: [ items: [
[ [
GeneratedFormTextField('appSourceURL', GeneratedFormTextField('appSourceURL',
@@ -325,7 +337,7 @@ class _AddAppPageState extends State<AddAppPage> {
); );
}); });
if (selectedUrls != null && selectedUrls.isNotEmpty) { if (selectedUrls != null && selectedUrls.isNotEmpty) {
changeUserInput(selectedUrls[0], true, false, isSearch: true); changeUserInput(selectedUrls[0], true, false, updateUrlInput: true);
} }
} }
} catch (e) { } catch (e) {

View File

@@ -145,6 +145,29 @@ class _AppPageState extends State<AppPage> {
appsProvider.saveApps([app.app]); 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

@@ -1,7 +1,11 @@
import 'dart:async';
import 'package:animations/animations.dart'; import 'package:animations/animations.dart';
import 'package:app_links/app_links.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/pages/add_app.dart'; import 'package:obtainium/pages/add_app.dart';
import 'package:obtainium/pages/apps.dart'; import 'package:obtainium/pages/apps.dart';
import 'package:obtainium/pages/import_export.dart'; import 'package:obtainium/pages/import_export.dart';
@@ -30,58 +34,119 @@ class _HomePageState extends State<HomePage> {
bool isReversing = false; bool isReversing = false;
int prevAppCount = -1; int prevAppCount = -1;
bool prevIsLoading = true; bool prevIsLoading = true;
late AppLinks _appLinks;
StreamSubscription<Uri>? _linkSubscription;
bool isLinkActivity = false;
List<NavigationPageItem> pages = [ List<NavigationPageItem> pages = [
NavigationPageItem(tr('appsString'), Icons.apps, NavigationPageItem(tr('appsString'), Icons.apps,
AppsPage(key: GlobalKey<AppsPageState>())), AppsPage(key: GlobalKey<AppsPageState>())),
NavigationPageItem(tr('addApp'), Icons.add, const AddAppPage()), NavigationPageItem(
tr('addApp'), Icons.add, AddAppPage(key: GlobalKey<AddAppPageState>())),
NavigationPageItem( NavigationPageItem(
tr('importExport'), Icons.import_export, const ImportExportPage()), tr('importExport'), Icons.import_export, const ImportExportPage()),
NavigationPageItem(tr('settings'), Icons.settings, const SettingsPage()) 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
AppsProvider appsProvider = context.watch<AppsProvider>(); AppsProvider appsProvider = context.watch<AppsProvider>();
SettingsProvider settingsProvider = context.watch<SettingsProvider>(); 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 && if (!prevIsLoading &&
prevAppCount >= 0 && prevAppCount >= 0 &&
appsProvider.apps.length > prevAppCount && appsProvider.apps.length > prevAppCount &&
selectedIndexHistory.isNotEmpty && selectedIndexHistory.isNotEmpty &&
selectedIndexHistory.last == 1) { selectedIndexHistory.last == 1 &&
!isLinkActivity) {
switchToPage(0); switchToPage(0);
} }
prevAppCount = appsProvider.apps.length; prevAppCount = appsProvider.apps.length;
@@ -129,6 +194,11 @@ class _HomePageState extends State<HomePage> {
), ),
), ),
onWillPop: () async { onWillPop: () async {
if (isLinkActivity &&
selectedIndexHistory.length == 1 &&
selectedIndexHistory.last == 1) {
return true;
}
setIsReversing(selectedIndexHistory.length >= 2 setIsReversing(selectedIndexHistory.length >= 2
? selectedIndexHistory.reversed.toList()[1] ? selectedIndexHistory.reversed.toList()[1]
: 0); : 0);
@@ -143,4 +213,10 @@ class _HomePageState extends State<HomePage> {
?.clearSelected(); ?.clearSelected();
}); });
} }
@override
void dispose() {
super.dispose();
_linkSubscription?.cancel();
}
} }

View File

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

View File

@@ -974,7 +974,7 @@ class AppsProvider with ChangeNotifier {
} }
} }
notifyListeners(); notifyListeners();
exportApps(isAuto: true); export(isAuto: true);
} }
Future<void> removeApps(List<String> appIds) async { Future<void> removeApps(List<String> appIds) async {
@@ -996,7 +996,7 @@ class AppsProvider with ChangeNotifier {
} }
if (appIds.isNotEmpty) { if (appIds.isNotEmpty) {
notifyListeners(); notifyListeners();
exportApps(isAuto: true); export(isAuto: true);
} }
} }
@@ -1173,7 +1173,7 @@ class AppsProvider with ChangeNotifier {
return updateAppIds; return updateAppIds;
} }
Future<String?> exportApps( Future<String?> export(
{bool pickOnly = false, isAuto = false, SettingsProvider? sp}) async { {bool pickOnly = false, isAuto = false, SettingsProvider? sp}) async {
SettingsProvider settingsProvider = sp ?? this.settingsProvider; SettingsProvider settingsProvider = sp ?? this.settingsProvider;
var exportDir = await settingsProvider.getExportDir(); var exportDir = await settingsProvider.getExportDir();
@@ -1203,12 +1203,22 @@ class AppsProvider with ChangeNotifier {
} }
String? returnPath; String? returnPath;
if (!pickOnly) { 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, var result = await saf.createFile(exportDir,
displayName: displayName:
'${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().toIso8601String().replaceAll(':', '-')}${isAuto ? '-auto' : ''}.json', '${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().toIso8601String().replaceAll(':', '-')}${isAuto ? '-auto' : ''}.json',
mimeType: 'application/json', mimeType: 'application/json',
bytes: Uint8List.fromList(utf8.encode( bytes: Uint8List.fromList(utf8.encode(jsonEncode(finalExport))));
jsonEncode(apps.values.map((e) => e.app.toJson()).toList()))));
if (result == null) { if (result == null) {
throw ObtainiumError(tr('unexpectedError')); throw ObtainiumError(tr('unexpectedError'));
} }
@@ -1218,10 +1228,13 @@ class AppsProvider with ChangeNotifier {
return returnPath; return returnPath;
} }
Future<int> importApps(String appsJSON) async { Future<MapEntry<int, bool>> import(String appsJSON) async {
List<App> importedApps = (jsonDecode(appsJSON) as List<dynamic>) var decodedJSON = jsonDecode(appsJSON);
.map((e) => App.fromJson(e)) var newFormat = decodedJSON is! List;
.toList(); List<App> importedApps =
((newFormat ? decodedJSON['apps'] : decodedJSON) as List<dynamic>)
.map((e) => App.fromJson(e))
.toList();
while (loadingApps) { while (loadingApps) {
await Future.delayed(const Duration(microseconds: 1)); await Future.delayed(const Duration(microseconds: 1));
} }
@@ -1232,7 +1245,20 @@ class AppsProvider with ChangeNotifier {
} }
await saveApps(importedApps, onlyIfExists: false); await saveApps(importedApps, onlyIfExists: false);
notifyListeners(); 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 @override

View File

@@ -213,7 +213,8 @@ class SettingsProvider with ChangeNotifier {
} }
String? getSettingString(String settingId) { 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) { void setSettingString(String settingId, String value) {
@@ -415,4 +416,13 @@ class SettingsProvider with ChangeNotifier {
prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val); prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val);
notifyListeners(); notifyListeners();
} }
bool get exportSettings {
return prefs?.getBool('exportSettings') ?? false;
}
set exportSettings(bool val) {
prefs?.setBool('exportSettings', val);
notifyListeners();
}
} }

View File

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

View File

@@ -5,10 +5,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: android_alarm_manager_plus name: android_alarm_manager_plus
sha256: "82fb28c867c4b3dd7e9157728e46426b8916362f977dbba46b949210f00099f4" sha256: "84720c8ad2758aabfbeafd24a8c355d8c8dd3aa52b01eaf3bb827c7210f61a91"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.4"
android_intent_plus: android_intent_plus:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -38,10 +38,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: animations name: animations
sha256: ef57563eed3620bd5d75ad96189846aca1e033c0c45fc9a7d26e80ab02b88a70 sha256: "708e4b68c23228c264b038fe7003a2f5d01ce85fc64d8cae090e86b27fcea6c5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted 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: archive:
dependency: transitive dependency: transitive
description: description:
@@ -94,10 +102,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cli_util name: cli_util
sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.4.0" version: "0.4.1"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@@ -142,10 +150,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5" sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3+7" version: "0.3.3+8"
crypto: crypto:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -350,6 +358,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.2.4" version: "8.2.4"
gtk:
dependency: transitive
description:
name: gtk
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
url: "https://pub.dev"
source: hosted
version: "2.1.0"
hsluv: hsluv:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -370,10 +386,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.2"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@@ -562,10 +578,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_html name: permission_handler_html
sha256: d96ff56a757b7f04fa825c469d296c5aebc55f743e87bd639fef91a466a24da8 sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.0+1" version: "0.1.0+2"
permission_handler_platform_interface: permission_handler_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -586,10 +602,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: petitparser name: petitparser
sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6 sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.1" version: "6.0.2"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@@ -767,10 +783,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: synchronized name: synchronized
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.0+1"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@@ -807,10 +823,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.1" version: "6.2.2"
url_launcher_android: url_launcher_android:
dependency: transitive dependency: transitive
description: description:
@@ -831,10 +847,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_linux name: url_launcher_linux
sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.1"
url_launcher_macos: url_launcher_macos:
dependency: transitive dependency: transitive
description: description:
@@ -855,26 +871,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7" sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "2.2.2"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_windows name: url_launcher_windows
sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.1"
uuid: uuid:
dependency: transitive dependency: transitive
description: description:
name: uuid name: uuid
sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.1" version: "4.2.2"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@@ -903,18 +919,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: "8326ee235f87605a2bfc444a4abc897f4abc78d83f054ba7d3d1074ce82b4fbf" sha256: e313dcdf45d4c95bcb8960351ef2389b7f0687b90bc92483f7f7983ae5758456
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.12.1" version: "3.13.0"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_platform_interface name: webview_flutter_platform_interface
sha256: adb8c03c2be231bea5a8ed0e9039e9d18dbb049603376beaefa15393ede468a5 sha256: "68e86162aa8fc646ae859e1585995c096c95fc2476881fa0c4a8d10f56013a5a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.7.0" version: "2.8.0"
webview_flutter_wkwebview: webview_flutter_wkwebview:
dependency: transitive dependency: transitive
description: description:
@@ -927,10 +943,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.0" version: "5.1.1"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:
@@ -951,10 +967,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: xml name: xml
sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.4.2" version: "6.5.0"
yaml: yaml:
dependency: transitive dependency: transitive
description: 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 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.14.35+229 # When changing this, update the tag in main() accordingly version: 0.14.38+232 # When changing this, update the tag in main() accordingly
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
@@ -67,6 +67,7 @@ dependencies:
connectivity_plus: ^5.0.0 connectivity_plus: ^5.0.0
shared_storage: ^0.8.0 shared_storage: ^0.8.0
crypto: ^3.0.3 crypto: ^3.0.3
app_links: ^3.5.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: