mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-28 20:13:28 +01:00 
			
		
		
		
	Compare commits
	
		
			68 Commits
		
	
	
		
			v0.13.1-be
			...
			v0.13.11-b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2eaf443359 | ||
|  | 5979957d60 | ||
|  | 049eb5914c | ||
|  | 7577f3ac9b | ||
|  | 5b05745b02 | ||
|  | 4366b4e369 | ||
|  | 9c60f10005 | ||
|  | a0d02043c4 | ||
|  | ff5152bf79 | ||
|  | 995a826917 | ||
|  | 2965e159cb | ||
|  | 0dcd5163d4 | ||
|  | d31bbd9ea8 | ||
|  | 423ba07fad | ||
|  | 3697d74185 | ||
|  | 038f089aac | ||
|  | ba3f512445 | ||
|  | 0fc1cff0a8 | ||
|  | 40bec4b732 | ||
|  | 8ca1e09c86 | ||
|  | e0c4ec5028 | ||
|  | 7fcba6c911 | ||
|  | 0186c00d97 | ||
|  | 9294540b5d | ||
|  | 0b16c28224 | ||
|  | 83028d405a | ||
|  | c4262c3eaa | ||
|  | f0e1831d30 | ||
|  | 9efd0dd46e | ||
|  | eb26c0be0b | ||
|  | 1ff1c6ca33 | ||
|  | 6169915e63 | ||
|  | a0d466a074 | ||
|  | 6f9ef6d51e | ||
|  | feb4c2eabc | ||
|  | c2cf39125d | ||
|  | 833ece1ef5 | ||
|  | fee23cadfa | ||
|  | 4c6303f783 | ||
|  | ce6e6c47db | ||
|  | 2ccff15525 | ||
|  | d24f2b4e6d | ||
|  | 03fc6a530f | ||
|  | 4136734a60 | ||
|  | ca1371260c | ||
|  | 03c2ce9a01 | ||
|  | eda5fec37c | ||
|  | e21c6297ff | ||
|  | c6297ea449 | ||
|  | e33cc00266 | ||
|  | 96c92c8df9 | ||
|  | e256ada2dc | ||
|  | eb0be196da | ||
|  | 1606ad3442 | ||
|  | d212f13345 | ||
|  | f80c9ec33e | ||
|  | 7681e23de9 | ||
|  | 22a60df40e | ||
|  | 431a01f2a5 | ||
|  | 0cd4385de7 | ||
|  | 0774b3ddc3 | ||
|  | b60b1ed058 | ||
|  | b196715d60 | ||
|  | 0673e90dff | ||
|  | 59cfa242fb | ||
|  | 65ab72ba90 | ||
|  | 408bca8951 | ||
|  | 480467492a | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-hdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-hdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-mdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-mdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 7.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 9.6 KiB | 
| @@ -26,6 +26,6 @@ subprojects { | ||||
|     project.evaluationDependsOn(':app') | ||||
| } | ||||
|  | ||||
| task clean(type: Delete) { | ||||
| tasks.register("clean", Delete) { | ||||
|     delete rootProject.buildDir | ||||
| } | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/graphics/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/graphics/icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 66 KiB | 
| @@ -121,12 +121,12 @@ | ||||
|     "followSystem": "System folgen", | ||||
|     "obtainium": "Obtainium", | ||||
|     "materialYou": "Material You", | ||||
|     "useBlackTheme": "Use Pure Black Dark Theme", | ||||
|     "useBlackTheme": "Verwende Pure Black Dark Theme", | ||||
|     "appSortBy": "App sortieren nach", | ||||
|     "authorName": "Autor/Name", | ||||
|     "nameAuthor": "Name/Autor", | ||||
|     "asAdded": "Wie hinzugefügt", | ||||
|     "appSortOrder": "App Sortierung nach", | ||||
|     "appSortOrder": "App sortieren nach", | ||||
|     "ascending": "Aufsteigend", | ||||
|     "descending": "Absteigend", | ||||
|     "bgUpdateCheckInterval": "Prüfintervall für Hintergrundaktualisierung", | ||||
| @@ -180,6 +180,7 @@ | ||||
|     "yesMarkUpdated": "Ja, als aktualisiert markieren", | ||||
|     "fdroid": "F-Droid Official", | ||||
|     "appIdOrName": "App ID oder Name", | ||||
|     "appId": "App ID", | ||||
|     "appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden", | ||||
|     "reposHaveMultipleApps": "Repos können mehrere Apps enthalten", | ||||
|     "fdroidThirdPartyRepo": "F-Droid Third-Party Repo", | ||||
| @@ -207,7 +208,7 @@ | ||||
|     "addCategory": "Kategorie hinzufügen", | ||||
|     "label": "Bezeichnung", | ||||
|     "language": "Sprache", | ||||
|     "copiedToClipboard": "Copied to Clipboard", | ||||
|     "copiedToClipboard": "In die Zwischenablage kopiert", | ||||
|     "storagePermissionDenied": "Speicherberechtigung verweigert", | ||||
|     "selectedCategorizeWarning": "Dadurch werden alle bestehenden Kategorieeinstellungen für die ausgewählten Apps ersetzt.", | ||||
|     "filterAPKsByRegEx": "APKs nach regulärem Ausdruck filtern", | ||||
| @@ -218,7 +219,7 @@ | ||||
|     "releaseDateAsVersionExplanation": "Diese Option sollte nur für Apps verwendet werden, bei denen die Versionserkennung nicht korrekt funktioniert, aber ein Veröffentlichungsdatum verfügbar ist.", | ||||
|     "changes": "Änderungen", | ||||
|     "releaseDate": "Veröffentlichungsdatum", | ||||
|     "importFromURLsInFile": "Importieren von URLs aus Datei ( z.B. OPML)", | ||||
|     "importFromURLsInFile": "Importieren von URLs aus Datei (z. B. OPML)", | ||||
|     "versionDetection": "Versionserkennung", | ||||
|     "standardVersionDetection": "Standardversionserkennung", | ||||
|     "groupByCategory": "Nach Kategorie gruppieren", | ||||
| @@ -227,11 +228,12 @@ | ||||
|     "dontShowAgain": "Nicht noch einmal zeigen", | ||||
|     "dontShowTrackOnlyWarnings": "Warnung für 'Nur Nachverfolgen' nicht anzeigen", | ||||
|     "dontShowAPKOriginWarnings": "Warnung für APK-Herkunft nicht anzeigen", | ||||
|     "moveNonInstalledAppsToBottom": "Move Non-Installed Apps to Bottom of Apps View", | ||||
|     "gitlabPATLabel": "GitLab Personal Access Token (Enables Search)", | ||||
|     "about": "About", | ||||
|     "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", | ||||
|     "checkOnStart": "Check Once on Start", | ||||
|     "moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben", | ||||
|     "gitlabPATLabel": "GitLab Personal Access Token (Aktiviert Suche)", | ||||
|     "about": "Über", | ||||
|     "requiresCredentialsInSettings": "Benötigt zusätzliche Anmeldedaten (in den Einstellungen)", | ||||
|     "checkOnStart": "Überprüfe einmalig beim Start", | ||||
|     "tryInferAppIdFromCode": "Try inferring App ID from source code", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "App entfernen?", | ||||
|         "other": "Apps entfernen?" | ||||
| @@ -258,7 +260,7 @@ | ||||
|     }, | ||||
|     "minute": { | ||||
|         "one": "{} Minute", | ||||
|         "other": "{} Minutes" | ||||
|         "other": "{} Minuten" | ||||
|     }, | ||||
|     "hour": { | ||||
|         "one": "{} Stunde", | ||||
|   | ||||
| @@ -180,6 +180,7 @@ | ||||
|     "yesMarkUpdated": "Yes, Mark as Updated", | ||||
|     "fdroid": "F-Droid Official", | ||||
|     "appIdOrName": "App ID or Name", | ||||
|     "appId": "App ID", | ||||
|     "appWithIdOrNameNotFound": "No App was found with that ID or Name", | ||||
|     "reposHaveMultipleApps": "Repos may contain multiple Apps", | ||||
|     "fdroidThirdPartyRepo": "F-Droid Third-Party Repo", | ||||
| @@ -232,6 +233,7 @@ | ||||
|     "about": "About", | ||||
|     "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", | ||||
|     "checkOnStart": "Check Once on Start", | ||||
|     "tryInferAppIdFromCode": "Try inferring App ID from source code", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Remove App?", | ||||
|         "other": "Remove Apps?" | ||||
|   | ||||
| @@ -180,6 +180,7 @@ | ||||
|     "yesMarkUpdated": "Sí, Marcar como Actualizada", | ||||
|     "fdroid": "Repositorio oficial de F-Droid", | ||||
|     "appIdOrName": "ID o Nombre de la Aplicación", | ||||
|     "appId": "ID de la Aplicación", | ||||
|     "appWithIdOrNameNotFound": "No se han encontrado aplicaciones con esa ID o nombre", | ||||
|     "reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones", | ||||
|     "fdroidThirdPartyRepo": "Rpositorios de terceros de F-Droid", | ||||
| @@ -232,6 +233,7 @@ | ||||
|     "about": "About", | ||||
|     "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", | ||||
|     "checkOnStart": "Check Once on Start", | ||||
|     "tryInferAppIdFromCode": "Try inferring App ID from source code", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "¿Eliminar Aplicación?", | ||||
|         "other": "¿Eliminar Aplicaciones?" | ||||
|   | ||||
| @@ -180,6 +180,7 @@ | ||||
|     "yesMarkUpdated": "بله، علامت گذاری به عنوان به روز شده", | ||||
|     "fdroid": "F-Droid Official", | ||||
|     "appIdOrName": "شناسه یا نام برنامه", | ||||
|     "appId": "App ID", | ||||
|     "appWithIdOrNameNotFound": "هیچ برنامه ای با آن شناسه یا نام یافت نشد", | ||||
|     "reposHaveMultipleApps": "مخازن ممکن است شامل چندین برنامه باشد", | ||||
|     "fdroidThirdPartyRepo": "مخازن شخص ثالث F-Droid", | ||||
| @@ -232,6 +233,7 @@ | ||||
|     "about": "About", | ||||
|     "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", | ||||
|     "checkOnStart": "Check Once on Start", | ||||
|     "tryInferAppIdFromCode": "Try inferring App ID from source code", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "برنامه حذف شود؟", | ||||
|         "other": "برنامه ها حذف شوند؟" | ||||
|   | ||||
| @@ -180,6 +180,7 @@ | ||||
|     "yesMarkUpdated": "Oui, marquer comme mis à jour", | ||||
|     "fdroid": "F-Droid Official", | ||||
|     "appIdOrName": "ID ou nom de l'application", | ||||
|     "appId": "ID de l'application", | ||||
|     "appWithIdOrNameNotFound": "Aucune application n'a été trouvée avec cet identifiant ou ce nom", | ||||
|     "reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications", | ||||
|     "fdroidThirdPartyRepo": "Dépôt tiers F-Droid", | ||||
| @@ -232,6 +233,7 @@ | ||||
|     "about": "About", | ||||
|     "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", | ||||
|     "checkOnStart": "Check Once on Start", | ||||
|     "tryInferAppIdFromCode": "Try inferring App ID from source code", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Supprimer l'application ?", | ||||
|         "other": "Supprimer les applications ?" | ||||
|   | ||||
| @@ -180,6 +180,7 @@ | ||||
|     "yesMarkUpdated": "Igen, megjelölés frissítettként", | ||||
|     "fdroid": "F-Droid Official", | ||||
|     "appIdOrName": "App ID vagy név", | ||||
|     "appId": "App ID", | ||||
|     "appWithIdOrNameNotFound": "Nem található app ezzel az azonosítóval vagy névvel", | ||||
|     "reposHaveMultipleApps": "A repók több alkalmazást is tartalmazhatnak", | ||||
|     "fdroidThirdPartyRepo": "F-Droid Harmadik-fél Repo", | ||||
| @@ -226,11 +227,12 @@ | ||||
|     "dontShowAgain": "Ne mutassa ezt újra", | ||||
|     "dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést", | ||||
|     "dontShowAPKOriginWarnings": "Ne jelenítsen meg az APK eredetére vonatkozó figyelmeztetéseket", | ||||
|     "moveNonInstalledAppsToBottom": "Move Non-Installed Apps to Bottom of Apps View", | ||||
|     "gitlabPATLabel": "GitLab Personal Access Token (Enables Search)", | ||||
|     "about": "About", | ||||
|     "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", | ||||
|     "checkOnStart": "Check Once on Start", | ||||
|     "moveNonInstalledAppsToBottom": "Helyezze át a nem telepített appokat az App nézet aljára", | ||||
|     "gitlabPATLabel": "GitLab Personal Access Token (Engedélyezi a Keresést)", | ||||
|     "about": "Rólunk", | ||||
|     "requiresCredentialsInSettings": "Ehhez további hitelesítő adatokra van szükség (a Beállításokban)", | ||||
|     "checkOnStart": "Egyszer az indításkor", | ||||
|     "tryInferAppIdFromCode": "Próbálja kikövetkeztetni az app azonosítót a forráskódból", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Eltávolítja az alkalmazást?", | ||||
|         "other": "Eltávolítja az alkalmazást?" | ||||
|   | ||||
| @@ -1,25 +1,25 @@ | ||||
| { | ||||
|     "invalidURLForSource": "URL dell'App da {} non valido", | ||||
|     "invalidURLForSource": "URL dell'app {} non valido", | ||||
|     "noReleaseFound": "Impossibile trovare una release adatta", | ||||
|     "noVersionFound": "Impossibile determinare la versione della release", | ||||
|     "urlMatchesNoSource": "L'URL non corrisponde ad alcuna fonte conosciuta", | ||||
|     "cantInstallOlderVersion": "Impossibile installare una versione precedente di un'App", | ||||
|     "appIdMismatch": "L'ID del pacchetto scaricato non corrisponde all'ID dell'App esistente", | ||||
|     "cantInstallOlderVersion": "Impossibile installare una versione precedente di un'app", | ||||
|     "appIdMismatch": "L'ID del pacchetto scaricato non corrisponde all'ID dell'app esistente", | ||||
|     "functionNotImplemented": "Questa classe non ha implementato questa funzione", | ||||
|     "placeholder": "Segnaposto", | ||||
|     "someErrors": "Si sono verificati degli errori", | ||||
|     "unexpectedError": "Errore imprevisto", | ||||
|     "ok": "Va bene", | ||||
|     "and": "e", | ||||
|     "startedBgUpdateTask": "Avviata l'attività di controllo degli aggiornamenti in background", | ||||
|     "bgUpdateIgnoreAfterIs": "Bg update ignoreAfter is {}", | ||||
|     "startedActualBGUpdateCheck": "Avviato il controllo effettivo degli aggiornamenti in background", | ||||
|     "bgUpdateTaskFinished": "Terminata l'attività di controllo degli aggiornamenti in background", | ||||
|     "startedBgUpdateTask": "Avviata l'attività di controllo degli aggiornamenti in secondo piano", | ||||
|     "bgUpdateIgnoreAfterIs": "Il parametro di agg. in secondo piano 'ignoreAfter' è {}", | ||||
|     "startedActualBGUpdateCheck": "Avviato il controllo effettivo degli aggiornamenti in secondo piano", | ||||
|     "bgUpdateTaskFinished": "Terminata l'attività di controllo degli aggiornamenti in secondo piano", | ||||
|     "firstRun": "Questo è il primo avvio di sempre di Obtainium", | ||||
|     "settingUpdateCheckIntervalTo": "Fissato intervallo di aggiornamento a {}", | ||||
|     "githubPATLabel": "GitHub Personal Access Token (diminuisce limite di traffico)", | ||||
|     "githubPATHint": "PAT deve seguire questo formato: username:token", | ||||
|     "githubPATFormat": "username:token", | ||||
|     "githubPATHint": "PAT deve seguire questo formato: nomeutente:token", | ||||
|     "githubPATFormat": "nomeutente:token", | ||||
|     "includePrereleases": "Includi prerelease", | ||||
|     "fallbackToOlderReleases": "Ripiega su release precedenti", | ||||
|     "filterReleaseTitlesByRegEx": "Filtra release con espressioni regolari", | ||||
| @@ -31,19 +31,19 @@ | ||||
|     "dropdownNoOptsError": "ERRORE: LA TENDINA DEVE AVERE ALMENO UN'OPZIONE", | ||||
|     "colour": "Colore", | ||||
|     "githubStarredRepos": "repository stellati da GitHub", | ||||
|     "uname": "Username", | ||||
|     "uname": "Nome utente", | ||||
|     "wrongArgNum": "Numero di argomenti forniti errato", | ||||
|     "xIsTrackOnly": "{} è in modalità Solo-Monitoraggio", | ||||
|     "source": "Fonte", | ||||
|     "app": "App", | ||||
|     "appsFromSourceAreTrackOnly": "Le App da questa fonte sono in modalità 'Solo-Monitoraggio'.", | ||||
|     "appsFromSourceAreTrackOnly": "Le app da questa fonte sono in modalità 'Solo-Monitoraggio'.", | ||||
|     "youPickedTrackOnly": "È stata selezionata l'opzione 'Solo-Monitoraggio'.", | ||||
|     "trackOnlyAppDescription": "L'App sarà monitorata per gli aggiornamenti, ma Obtainium non sarà in grado di scaricarli o di installarli.", | ||||
|     "trackOnlyAppDescription": "L'app sarà monitorata per gli aggiornamenti, ma Obtainium non sarà in grado di scaricarli o di installarli.", | ||||
|     "cancelled": "Annullato", | ||||
|     "appAlreadyAdded": "App già aggiunta", | ||||
|     "alreadyUpToDateQuestion": "L'App è già aggiornata?", | ||||
|     "addApp": "Aggiungi App", | ||||
|     "appSourceURL": "URL della fonte dell'App", | ||||
|     "alreadyUpToDateQuestion": "L'app è già aggiornata?", | ||||
|     "addApp": "Aggiungi app", | ||||
|     "appSourceURL": "URL della fonte dell'app", | ||||
|     "error": "Errore", | ||||
|     "add": "Aggiungi", | ||||
|     "searchSomeSourcesLabel": "Cerca (solo per alcune fonti)", | ||||
| @@ -53,10 +53,10 @@ | ||||
|     "trackOnlyInBrackets": "(Solo-Monitoraggio)", | ||||
|     "searchableInBrackets": "(ricercabile)", | ||||
|     "appsString": "App", | ||||
|     "noApps": "Nessuna App", | ||||
|     "noAppsForFilter": "Nessuna App per i filtri selezionati", | ||||
|     "noApps": "Nessuna app", | ||||
|     "noAppsForFilter": "Nessuna app per i filtri selezionati", | ||||
|     "byX": "Di {}", | ||||
|     "percentProgress": "Progresso: {}%", | ||||
|     "percentProgress": "Avanzamento: {}%", | ||||
|     "pleaseWait": "In attesa", | ||||
|     "updateAvailable": "Aggiornamento disponibile", | ||||
|     "estimateInBracketsShort": "(prev.)", | ||||
| @@ -65,31 +65,31 @@ | ||||
|     "selectAll": "Seleziona tutto", | ||||
|     "deselectN": "Deseleziona {}", | ||||
|     "xWillBeRemovedButRemainInstalled": "Verà effettuata la rimozione di {}, ma non la disinstallazione.", | ||||
|     "removeSelectedAppsQuestion": "Rimuovere le App selezionate?", | ||||
|     "removeSelectedApps": "Rimuovi le App selezionate", | ||||
|     "removeSelectedAppsQuestion": "Rimuovere le app selezionate?", | ||||
|     "removeSelectedApps": "Rimuovi le app selezionate", | ||||
|     "updateX": "Aggiorna {}", | ||||
|     "installX": "Installa {}", | ||||
|     "markXTrackOnlyAsUpdated": "Contrassegna {}\n(Solo-Monitoraggio)\ncome aggiornato", | ||||
|     "changeX": "Modifica {}", | ||||
|     "installUpdateApps": "Installa/Aggiorna App", | ||||
|     "installUpdateSelectedApps": "Installa/Aggiorna le App selezionate", | ||||
|     "markXSelectedAppsAsUpdated": "Contrassegnare le {} App selezionate come aggiornate?", | ||||
|     "installUpdateApps": "Installa/Aggiorna app", | ||||
|     "installUpdateSelectedApps": "Installa/Aggiorna le app selezionate", | ||||
|     "markXSelectedAppsAsUpdated": "Contrassegnare le {} app selezionate come aggiornate?", | ||||
|     "no": "No", | ||||
|     "yes": "Sì", | ||||
|     "markSelectedAppsUpdated": "Contrassegna le App selezionate come aggiornate", | ||||
|     "markSelectedAppsUpdated": "Contrassegna le app selezionate come aggiornate", | ||||
|     "pinToTop": "Fissa in alto", | ||||
|     "unpinFromTop": "Rimuovi dall'alto", | ||||
|     "resetInstallStatusForSelectedAppsQuestion": "Ripristinare lo stato d'installazione delle App selezionate?", | ||||
|     "installStatusOfXWillBeResetExplanation": "Lo stato d'installazione di ogni App selezionata sarà ripristinato.\n\nCiò può essere d'aiuto nel caso in cui la versione mostrata dell'App in Obtainium non è corretta a causa di un aggiornamento fallito o di altri problemi.", | ||||
|     "shareSelectedAppURLs": "Condividi gli URL delle App selezionate", | ||||
|     "resetInstallStatusForSelectedAppsQuestion": "Ripristinare lo stato d'installazione delle app selezionate?", | ||||
|     "installStatusOfXWillBeResetExplanation": "Lo stato d'installazione di ogni app selezionata sarà ripristinato.\n\nCiò può essere d'aiuto nel caso in cui la versione mostrata dell'app in Obtainium non sia corretta a causa di un aggiornamento fallito o di altri problemi.", | ||||
|     "shareSelectedAppURLs": "Condividi gli URL delle app selezionate", | ||||
|     "resetInstallStatus": "Ripristina lo stato d'installazione", | ||||
|     "more": "Di più", | ||||
|     "removeOutdatedFilter": "Rimuovi il filtro per le App non aggiornate", | ||||
|     "showOutdatedOnly": "Mostra solo le App non aggiornate", | ||||
|     "more": "Altro", | ||||
|     "removeOutdatedFilter": "Rimuovi il filtro per le app non aggiornate", | ||||
|     "showOutdatedOnly": "Mostra solo le app non aggiornate", | ||||
|     "filter": "Filtri", | ||||
|     "filterActive": "Filtri *", | ||||
|     "filterApps": "Filtra App", | ||||
|     "appName": "Nome dell'App", | ||||
|     "filterApps": "Filtra app", | ||||
|     "appName": "Nome dell'app", | ||||
|     "author": "Autore", | ||||
|     "upToDateApps": "App aggiornate", | ||||
|     "nonInstalledApps": "App non installate", | ||||
| @@ -102,14 +102,14 @@ | ||||
|     "obtainiumImport": "Importa in Obtainium", | ||||
|     "importFromURLList": "Importa da lista di URL", | ||||
|     "searchQuery": "Stringa di ricerca", | ||||
|     "appURLList": "Lista di URL delle App", | ||||
|     "appURLList": "Lista di URL delle app", | ||||
|     "line": "Linea", | ||||
|     "searchX": "Cerca su {}", | ||||
|     "noResults": "Nessun risultato trovato", | ||||
|     "importX": "Importa {}", | ||||
|     "importedAppsIdDisclaimer": "Le App importate potrebbero essere visualizzate erroneamente come \"Non installate\".\nPer risolvere il problema, reinstallale con Obtainium.\nQuesto non dovrebbe influire sui dati delle App.\n\nRiguarda solo l'URL e i metodi di importazione di terze parti.", | ||||
|     "importErrors": "Errori dell'importazione", | ||||
|     "importedXOfYApps": "{} App di {} importate.", | ||||
|     "importedAppsIdDisclaimer": "Le app importate potrebbero essere visualizzate erroneamente come \"Non installate\".\nPer risolvere il problema, reinstallale con Obtainium.\nCiò non dovrebbe influire sui dati delle app.\n\nRiguarda solo l'URL e i metodi di importazione di terze parti.", | ||||
|     "importErrors": "Errori di importazione", | ||||
|     "importedXOfYApps": "{} app di {} importate.", | ||||
|     "followingURLsHadErrors": "I seguenti URL contengono errori:", | ||||
|     "okay": "Va bene", | ||||
|     "selectURL": "Seleziona l'URL", | ||||
| @@ -118,27 +118,27 @@ | ||||
|     "theme": "Tema", | ||||
|     "dark": "Scuro", | ||||
|     "light": "Chiaro", | ||||
|     "followSystem": "Segui sistema", | ||||
|     "followSystem": "Segui il sistema", | ||||
|     "obtainium": "Obtainium", | ||||
|     "materialYou": "Material You", | ||||
|     "useBlackTheme": "Use Pure Black Dark Theme", | ||||
|     "useBlackTheme": "Usa il tema Nero puro", | ||||
|     "appSortBy": "App ordinate per", | ||||
|     "authorName": "Autore/Nome", | ||||
|     "nameAuthor": "Nome/Autore", | ||||
|     "asAdded": "Data di aggiunta", | ||||
|     "appSortOrder": "Ordinamento", | ||||
|     "appSortOrder": "Ordine", | ||||
|     "ascending": "Ascendente", | ||||
|     "descending": "Discendente", | ||||
|     "bgUpdateCheckInterval": "Intervallo di controllo degli aggiornamenti in background", | ||||
|     "bgUpdateCheckInterval": "Intervallo di controllo degli aggiornamenti in secondo piano", | ||||
|     "neverManualOnly": "Mai - Solo manuale", | ||||
|     "appearance": "Aspetto", | ||||
|     "showWebInAppView": "Mostra pagina web dell'App se selezionata", | ||||
|     "showWebInAppView": "Mostra pagina web dell'app se selezionata", | ||||
|     "pinUpdates": "Fissa aggiornamenti disponibili in alto", | ||||
|     "updates": "Aggiornamenti", | ||||
|     "sourceSpecific": "Specifiche per la fonte", | ||||
|     "appSource": "Sorgente dell'App", | ||||
|     "appSource": "Sorgente dell'app", | ||||
|     "noLogs": "Nessun log", | ||||
|     "appLogs": "Log dell'App", | ||||
|     "appLogs": "Log dell'app", | ||||
|     "close": "Chiudi", | ||||
|     "share": "Condividi", | ||||
|     "appNotFound": "App non trovata", | ||||
| @@ -148,28 +148,28 @@ | ||||
|     "deviceSupportsXArch": "Il dispositivo in uso supporta l'architettura {} della CPU.", | ||||
|     "deviceSupportsFollowingArchs": "Il dispositivo in uso supporta le seguenti architetture della CPU:", | ||||
|     "warning": "Attenzione", | ||||
|     "sourceIsXButPackageFromYPrompt": "L'origine dell'App è '{}' ma il pacchetto della release proviene da '{}'. Continuare?", | ||||
|     "sourceIsXButPackageFromYPrompt": "L'origine dell'app è '{}' ma il pacchetto della release proviene da '{}'. Continuare?", | ||||
|     "updatesAvailable": "Aggiornamenti disponibili", | ||||
|     "updatesAvailableNotifDescription": "Notifica all'utente che sono disponibili gli aggiornamenti di una o più App monitorate da Obtainium", | ||||
|     "updatesAvailableNotifDescription": "Notifica all'utente che sono disponibili gli aggiornamenti di una o più app monitorate da Obtainium", | ||||
|     "noNewUpdates": "Nessun nuovo aggiornamento.", | ||||
|     "xHasAnUpdate": "Aggiornamento disponibile per {}", | ||||
|     "appsUpdated": "App aggiornate", | ||||
|     "appsUpdatedNotifDescription": "Notifica all'utente che una o più App sono state aggiornate in background", | ||||
|     "xWasUpdatedToY": "{} è stato aggiornato a {}.", | ||||
|     "appsUpdatedNotifDescription": "Notifica all'utente che una o più app sono state aggiornate in secondo piano", | ||||
|     "xWasUpdatedToY": "{} è stato aggiornato alla {}.", | ||||
|     "errorCheckingUpdates": "Controllo degli errori per gli aggiornamenti", | ||||
|     "errorCheckingUpdatesNotifDescription": "Una notifica che mostra quando il controllo degli aggiornamenti in background fallisce", | ||||
|     "errorCheckingUpdatesNotifDescription": "Una notifica che mostra quando il controllo degli aggiornamenti in secondo piano fallisce", | ||||
|     "appsRemoved": "App rimosse", | ||||
|     "appsRemovedNotifDescription": "Notifica all'utente che una o più App sono state rimosse a causa di errori durante il caricamento", | ||||
|     "appsRemovedNotifDescription": "Notifica all'utente che una o più app sono state rimosse a causa di errori durante il caricamento", | ||||
|     "xWasRemovedDueToErrorY": "{} è stata rimosso a causa di questo errore: {}", | ||||
|     "completeAppInstallation": "Completa l'installazione dell'App", | ||||
|     "obtainiumMustBeOpenToInstallApps": "Obtainium deve essere aperto per poter installare le App", | ||||
|     "completeAppInstallationNotifDescription": "Chiede all'utente di riaprire Obtainium per terminare l'installazione di un App", | ||||
|     "completeAppInstallation": "Completa l'installazione dell'app", | ||||
|     "obtainiumMustBeOpenToInstallApps": "Obtainium deve essere aperto per poter installare le app", | ||||
|     "completeAppInstallationNotifDescription": "Chiede all'utente di riaprire Obtainium per terminare l'installazione di un'app", | ||||
|     "checkingForUpdates": "Controllo degli aggiornamenti in corso", | ||||
|     "checkingForUpdatesNotifDescription": "Notifica transitoria che appare durante la verifica degli aggiornamenti", | ||||
|     "pleaseAllowInstallPerm": "Per favore permetti a Obtainium di installare le App", | ||||
|     "pleaseAllowInstallPerm": "Per favore permetti a Obtainium di installare le app", | ||||
|     "trackOnly": "Solo-Monitoraggio", | ||||
|     "errorWithHttpStatusCode": "Errore {}", | ||||
|     "versionCorrectionDisabled": "Correzione della versione disabilitata (il plugin non pare funzionare)", | ||||
|     "versionCorrectionDisabled": "Correzione della versione disattivata (il plugin sembra non funzionare)", | ||||
|     "unknown": "Sconosciuto", | ||||
|     "none": "Nessuno", | ||||
|     "never": "Mai", | ||||
| @@ -178,10 +178,11 @@ | ||||
|     "lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}", | ||||
|     "remove": "Rimuovi", | ||||
|     "yesMarkUpdated": "Sì, contrassegna come aggiornato", | ||||
|     "fdroid": "F-Droid Official", | ||||
|     "appIdOrName": "ID o nome dell'App", | ||||
|     "appWithIdOrNameNotFound": "Non è stata trovata alcuna App con quell'ID o nome", | ||||
|     "reposHaveMultipleApps": "I repository possono contenere più App", | ||||
|     "fdroid": "F-Droid ufficiale", | ||||
|     "appIdOrName": "ID o nome dell'app", | ||||
|     "appId": "ID dell'app", | ||||
|     "appWithIdOrNameNotFound": "Non è stata trovata alcuna app con quell'ID o nome", | ||||
|     "reposHaveMultipleApps": "I repository possono contenere più app", | ||||
|     "fdroidThirdPartyRepo": "Repository F-Droid di terze parti", | ||||
|     "steam": "Steam", | ||||
|     "steamMobile": "Steam Mobile", | ||||
| @@ -192,9 +193,9 @@ | ||||
|     "markUpdated": "Contrassegna come aggiornato", | ||||
|     "additionalOptions": "Opzioni aggiuntive", | ||||
|     "disableVersionDetection": "Disattiva il rilevamento della versione", | ||||
|     "noVersionDetectionExplanation": "Questa opzione dovrebbe essere usata solo per le App la cui versione non viene rilevata correttamente.", | ||||
|     "noVersionDetectionExplanation": "Questa opzione dovrebbe essere usata solo per le app la cui versione non viene rilevata correttamente.", | ||||
|     "downloadingX": "Scaricamento di {} in corso", | ||||
|     "downloadNotifDescription": "Notifica all'utente lo stato di avanzamento del download di un'App", | ||||
|     "downloadNotifDescription": "Notifica all'utente lo stato di avanzamento del download di un'app", | ||||
|     "noAPKFound": "Nessun APK trovato", | ||||
|     "noVersionDetection": "Disattiva rilevamento di versione", | ||||
|     "categorize": "Aggiungi a categoria", | ||||
| @@ -203,19 +204,19 @@ | ||||
|     "noCategory": "Nessuna categoria", | ||||
|     "noCategories": "Nessuna categoria", | ||||
|     "deleteCategoriesQuestion": "Eliminare le categorie?", | ||||
|     "categoryDeleteWarning": "Tutte le App nelle categorie eliminate saranno impostate come non categorizzate.", | ||||
|     "categoryDeleteWarning": "Tutte le app nelle categorie eliminate saranno impostate come non categorizzate.", | ||||
|     "addCategory": "Aggiungi categoria", | ||||
|     "label": "Etichetta", | ||||
|     "language": "Lingua", | ||||
|     "copiedToClipboard": "Copiato negli appunti", | ||||
|     "storagePermissionDenied": "Accesso ai file non autorizzato", | ||||
|     "selectedCategorizeWarning": "Ciò sostituirà le impostazioni di categoria esistenti per le App selezionate.", | ||||
|     "selectedCategorizeWarning": "Ciò sostituirà le impostazioni di categoria esistenti per le app selezionate.", | ||||
|     "filterAPKsByRegEx": "Filtra file APK con espressioni regolari", | ||||
|     "removeFromObtainium": "Rimuovi da Obtainium", | ||||
|     "uninstallFromDevice": "Disinstalla dal dispositivo", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Funziona solo per le App con il rilevamento della versione disattivato.", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Funziona solo per le app con il rilevamento della versione disattivato.", | ||||
|     "releaseDateAsVersion": "Usa data di rilascio come versione", | ||||
|     "releaseDateAsVersionExplanation": "Questa opzione dovrebbe essere usata solo per le App in cui il rilevamento della versione non funziona correttamente, ma è disponibile una data di rilascio.", | ||||
|     "releaseDateAsVersionExplanation": "Questa opzione dovrebbe essere usata solo per le app in cui il rilevamento della versione non funziona correttamente, ma è disponibile una data di rilascio.", | ||||
|     "changes": "Novità", | ||||
|     "releaseDate": "Data di rilascio", | ||||
|     "importFromURLsInFile": "Importa da URL in file (come OPML)", | ||||
| @@ -223,34 +224,35 @@ | ||||
|     "standardVersionDetection": "Rilevamento di versione standard", | ||||
|     "groupByCategory": "Raggruppa per categoria", | ||||
|     "autoApkFilterByArch": "Tenta di filtrare gli APK in base all'architettura della CPU, se possibile", | ||||
|     "overrideSource": "Override Source", | ||||
|     "dontShowAgain": "Don't show this again", | ||||
|     "dontShowTrackOnlyWarnings": "Don't Show the 'Track-Only' Warning", | ||||
|     "dontShowAPKOriginWarnings": "Don't Show APK Origin Warnings", | ||||
|     "moveNonInstalledAppsToBottom": "Move Non-Installed Apps to Bottom of Apps View", | ||||
|     "gitlabPATLabel": "GitLab Personal Access Token (Enables Search)", | ||||
|     "about": "About", | ||||
|     "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", | ||||
|     "checkOnStart": "Check Once on Start", | ||||
|     "overrideSource": "Sovrascrivi fonte", | ||||
|     "dontShowAgain": "Non mostrarlo più", | ||||
|     "dontShowTrackOnlyWarnings": "Non mostrare gli avvisi 'Solo-Monitoraggio'", | ||||
|     "dontShowAPKOriginWarnings": "Non mostrare gli avvisi di origine dell'APK", | ||||
|     "moveNonInstalledAppsToBottom": "Sposta le app non installate in fondo alla lista", | ||||
|     "gitlabPATLabel": "GitLab Personal Access Token (attiva la ricerca)", | ||||
|     "about": "Informazioni", | ||||
|     "requiresCredentialsInSettings": "Servono credenziali aggiuntive (in Impostazioni)", | ||||
|     "checkOnStart": "Controlla una volta all'avvio", | ||||
|     "tryInferAppIdFromCode": "Prova a dedurre l'ID dell'app dal codice sorgente", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Rimuovere l'App?", | ||||
|         "other": "Rimuovere le App?" | ||||
|         "one": "Rimuovere l'app?", | ||||
|         "other": "Rimuovere le app?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Troppe richieste (traffico limitato) - riprova tra {} minuto", | ||||
|         "other": "Troppe richieste (traffico limitato) - riprova tra {} minuti" | ||||
|     }, | ||||
|     "bgUpdateGotErrorRetryInMinutes": { | ||||
|         "one": "Il controllo degli aggiornamenti in background ha incontrato un {}, nuovo tentativo tra {} minuto", | ||||
|         "other": "Il controllo degli aggiornamenti in background ha incontrato un {}, nuovo tentativo tra {} minuti" | ||||
|         "one": "Il controllo degli aggiornamenti in secondo piano ha riscontrato un {}, nuovo tentativo tra {} minuto", | ||||
|         "other": "Il controllo degli aggiornamenti in secondo piano ha riscontrato un {}, nuovo tentativo tra {} minuti" | ||||
|     }, | ||||
|     "bgCheckFoundUpdatesWillNotifyIfNeeded": { | ||||
|         "one": "Il controllo degli aggiornamenti in background ha trovato {} aggiornamento - notificherà l'utente se necessario", | ||||
|         "other": "Il controllo degli aggiornamenti in background ha trovato {} aggiornamenti - notificherà l'utente se necessario" | ||||
|         "one": "Il controllo degli aggiornamenti in secondo piano ha trovato {} aggiornamento - notificherà l'utente se necessario", | ||||
|         "other": "Il controllo degli aggiornamenti in secondo piano ha trovato {} aggiornamenti - notificherà l'utente se necessario" | ||||
|     }, | ||||
|     "apps": { | ||||
|         "one": "{} App", | ||||
|         "other": "{} App" | ||||
|         "one": "{} app", | ||||
|         "other": "{} app" | ||||
|     }, | ||||
|     "url": { | ||||
|         "one": "{} URL", | ||||
| @@ -269,15 +271,15 @@ | ||||
|         "other": "{} giorni" | ||||
|     }, | ||||
|     "clearedNLogsBeforeXAfterY": { | ||||
|         "one": "Pulito {n} log (prima = {before}, dopo = {after})", | ||||
|         "other": "Puliti {n} log (prima = {before}, dopo = {after})" | ||||
|         "one": "Rimosso {n} log (prima = {before}, dopo = {after})", | ||||
|         "other": "Rimossi {n} log (prima = {before}, dopo = {after})" | ||||
|     }, | ||||
|     "xAndNMoreUpdatesAvailable": { | ||||
|         "one": "{} e un'altra App hanno aggiornamenti disponibili.", | ||||
|         "other": "{} e altre {} App hanno aggiornamenti disponibili." | ||||
|         "one": "{} e un'altra app hanno aggiornamenti disponibili.", | ||||
|         "other": "{} e altre {} app hanno aggiornamenti disponibili." | ||||
|     }, | ||||
|     "xAndNMoreUpdatesInstalled": { | ||||
|         "one": "{} e un'altra App sono state aggiornate.", | ||||
|         "other": "{} e altre {} App sono state aggiornate." | ||||
|         "one": "{} e un'altra app sono state aggiornate.", | ||||
|         "other": "{} e altre {} app sono state aggiornate." | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -180,6 +180,7 @@ | ||||
|     "yesMarkUpdated": "はい、アップデート済みとしてマークします", | ||||
|     "fdroid": "F-Droid Official", | ||||
|     "appIdOrName": "アプリのIDまたは名前", | ||||
|     "appId": "App ID", | ||||
|     "appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした", | ||||
|     "reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります", | ||||
|     "fdroidThirdPartyRepo": "F-Droid サードパーティリポジトリ", | ||||
| @@ -232,6 +233,7 @@ | ||||
|     "about": "概要", | ||||
|     "requiresCredentialsInSettings": "これには追加の認証が必要です (設定にて)", | ||||
|     "checkOnStart": "Check Once on Start", | ||||
|     "tryInferAppIdFromCode": "Try inferring App ID from source code", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "アプリを削除しますか?", | ||||
|         "other": "アプリを削除しますか?" | ||||
|   | ||||
							
								
								
									
										285
									
								
								assets/translations/pl.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								assets/translations/pl.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | ||||
| { | ||||
|     "noDescription": "Brak opisu", | ||||
|     "no": "Nie", | ||||
|     "okay": "Okej", | ||||
|     "appId": "ID aplikacji", | ||||
|     "bgUpdateGotErrorRetryInMinutes": { | ||||
|         "one": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} min.", | ||||
|         "other": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} min." | ||||
|     }, | ||||
|     "invalidURLForSource": "Nieprawidłowy adres URL aplikacji {}", | ||||
|     "noReleaseFound": "Nie można znaleźć odpowiedniego wydania", | ||||
|     "noVersionFound": "Nie można określić wersji wydania", | ||||
|     "urlMatchesNoSource": "Adres URL nie pasuje do znanego źródła", | ||||
|     "cantInstallOlderVersion": "Nie można zainstalować starszej wersji aplikacji", | ||||
|     "appIdMismatch": "Pobrany identyfikator pakietu nie pasuje do istniejącego identyfikatora aplikacji", | ||||
|     "functionNotImplemented": "Ta klasa nie zaimplementowała tej funkcji", | ||||
|     "placeholder": "Placeholder", | ||||
|     "someErrors": "Wystąpiły pewne błędy", | ||||
|     "unexpectedError": "Nieoczekiwany błąd", | ||||
|     "ok": "Okej", | ||||
|     "and": "i", | ||||
|     "startedBgUpdateTask": "Rozpoczęto zadanie sprawdzania aktualizacji w tle", | ||||
|     "bgUpdateIgnoreAfterIs": "Parametr ignoreAfter aktualizacji w tle to {}", | ||||
|     "startedActualBGUpdateCheck": "Rozpoczęto sprawdzanie aktualizacji w tle", | ||||
|     "bgUpdateTaskFinished": "Zakończono zadanie sprawdzania aktualizacji w tle", | ||||
|     "firstRun": "Jest to pierwsze uruchomienie Obtainium", | ||||
|     "settingUpdateCheckIntervalTo": "Ustawianie interwału aktualizacji na {}", | ||||
|     "githubPATLabel": "Osobisty Token Dostępu GitHub (zwiększa limit zapytań)", | ||||
|     "githubPATHint": "Wymagany format OTD: użytkownik:token", | ||||
|     "githubPATFormat": "użytkownik:token", | ||||
|     "includePrereleases": "Uwzględnij wersje wstępne", | ||||
|     "fallbackToOlderReleases": "Powracaj do starszych wersji", | ||||
|     "filterReleaseTitlesByRegEx": "Filtruj tytuły wydań wg. wyrażeń regularnych", | ||||
|     "invalidRegEx": "Nieprawidłowe wyrażenie regularne", | ||||
|     "cancel": "Anuluj", | ||||
|     "continue": "Kontynuuj", | ||||
|     "requiredInBrackets": "(Wymagane)", | ||||
|     "dropdownNoOptsError": "BŁĄD: LISTA ROZWIJANA MUSI MIEĆ CO NAJMNIEJ JEDNĄ OPCJĘ", | ||||
|     "colour": "Kolor", | ||||
|     "githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką", | ||||
|     "uname": "Nazwa użytkownika", | ||||
|     "wrongArgNum": "Nieprawidłowa liczba podanych argumentów", | ||||
|     "xIsTrackOnly": "{} jest tylko obserwowana", | ||||
|     "source": "Źródło", | ||||
|     "app": "Aplikacja", | ||||
|     "appsFromSourceAreTrackOnly": "Aplikacje z tego źródła są „Obserwowane”.", | ||||
|     "youPickedTrackOnly": "Wybrano opcję „Tylko obserwuj”.", | ||||
|     "trackOnlyAppDescription": "Aplikacja będzie obserwowana pod kątem aktualizacji, ale Obtainium nie będzie w stanie jej pobrać ani zainstalować.", | ||||
|     "cancelled": "Anulowano", | ||||
|     "appAlreadyAdded": "Aplikacja już została dodana", | ||||
|     "alreadyUpToDateQuestion": "Aplikacja jest już aktualna?", | ||||
|     "addApp": "Dodaj apkę", | ||||
|     "appSourceURL": "Adres URL źródła aplikacji", | ||||
|     "error": "Błąd", | ||||
|     "add": "Dodaj", | ||||
|     "searchSomeSourcesLabel": "Szukaj (tylko niektóre źródła)", | ||||
|     "search": "Szukaj", | ||||
|     "additionalOptsFor": "Dodatkowe opcje dla {}", | ||||
|     "supportedSourcesBelow": "Obsługiwane źródła:", | ||||
|     "trackOnlyInBrackets": "(tylko obserwowane)", | ||||
|     "searchableInBrackets": "(Wyszukiwalne)", | ||||
|     "appsString": "Aplikacje", | ||||
|     "noApps": "Brak aplikacji", | ||||
|     "noAppsForFilter": "Brak aplikacji dla filtra", | ||||
|     "byX": "Autorstwa {}", | ||||
|     "percentProgress": "Postęp: {}%", | ||||
|     "pleaseWait": "Proszę czekać", | ||||
|     "updateAvailable": "Dostępna aktualizacja", | ||||
|     "estimateInBracketsShort": "(Szac.)", | ||||
|     "notInstalled": "Nie zainstalowano", | ||||
|     "estimateInBrackets": "(Szacunkowo)", | ||||
|     "selectAll": "Zaznacz wszystkie", | ||||
|     "deselectN": "Odznacz {}", | ||||
|     "xWillBeRemovedButRemainInstalled": "{} zostanie usunięty z Obtainium, ale pozostanie zainstalowany na urządzeniu.", | ||||
|     "removeSelectedAppsQuestion": "Usunąć wybrane aplikacje?", | ||||
|     "removeSelectedApps": "Usuń wybrane aplikacje", | ||||
|     "updateX": "Zaktualizuj {}", | ||||
|     "installX": "Zainstaluj {}", | ||||
|     "markXTrackOnlyAsUpdated": "Oznacz {}\n(Tylko obserwowana)\njako zaktualizowaną", | ||||
|     "changeX": "Zmień {}", | ||||
|     "installUpdateApps": "Instaluj/aktualizuj aplikacje", | ||||
|     "installUpdateSelectedApps": "Zainstaluj/zaktualizuj wybrane aplikacje", | ||||
|     "markXSelectedAppsAsUpdated": "Oznaczyć {} wybranych aplikacji jako zaktualizowane?", | ||||
|     "yes": "Tak", | ||||
|     "markSelectedAppsUpdated": "Oznacz wybrane aplikacje jako zaktualizowane", | ||||
|     "pinToTop": "Przypnij", | ||||
|     "unpinFromTop": "Odepnij", | ||||
|     "resetInstallStatusForSelectedAppsQuestion": "Zresetować status instalacji dla wybranych aplikacji?", | ||||
|     "installStatusOfXWillBeResetExplanation": "Stan instalacji wybranych aplikacji zostanie zresetowany.\n\nMoże być to pomocne, gdy wersja aplikacji wyświetlana w Obtainium jest nieprawidłowa z powodu nieudanych aktualizacji lub innych problemów.", | ||||
|     "shareSelectedAppURLs": "Udostępnij wybrane adresy URL aplikacji", | ||||
|     "resetInstallStatus": "Zresetuj stan instalacji", | ||||
|     "more": "Więcej", | ||||
|     "removeOutdatedFilter": "Usuń filtr nieaktualnych aplikacji", | ||||
|     "showOutdatedOnly": "Pokaż tylko nieaktualne aplikacje", | ||||
|     "filter": "FIltr", | ||||
|     "filterActive": "Filtruj *", | ||||
|     "filterApps": "Filtruj aplikacje", | ||||
|     "appName": "Nazwa aplikacji", | ||||
|     "author": "Autor", | ||||
|     "upToDateApps": "Aktualne aplikacje", | ||||
|     "nonInstalledApps": "Niezainstalowane aplikacje", | ||||
|     "importExport": "Import/Eksport", | ||||
|     "settings": "Ustawienia", | ||||
|     "exportedTo": "Wyeksportowano do {}", | ||||
|     "obtainiumExport": "Eksportuj Obtainium", | ||||
|     "invalidInput": "Nieprawidłowe wprowadzenie", | ||||
|     "importedX": "Zaimportowano {}", | ||||
|     "obtainiumImport": "Import Obtainium", | ||||
|     "importFromURLList": "Importuj z listy adresów URL", | ||||
|     "searchQuery": "Wyszukiwane zapytanie", | ||||
|     "appURLList": "Lista adresów URL aplikacji", | ||||
|     "line": "Linia", | ||||
|     "searchX": "Przeszukaj {}", | ||||
|     "noResults": "Nie znaleziono wyników", | ||||
|     "importX": "Importuj {}", | ||||
|     "importedAppsIdDisclaimer": "Zaimportowane aplikacje mogą być wyświetlane jako „Niezainstalowane”.\nAby to naprawić, zainstaluj je ponownie za pomocą Obtainium.\nNie powinno to mieć wpływu na dane aplikacji.\n\nDotyczy tylko adresów URL i metod importu innych aplikacji.", | ||||
|     "importErrors": "Błędy importowania", | ||||
|     "importedXOfYApps": "Zaimportowano {} z {} aplikacji.", | ||||
|     "followingURLsHadErrors": "Następujące adresy URL zawierały błędy:", | ||||
|     "selectURL": "Wybierz adres URL", | ||||
|     "selectURLs": "Wybierz adresy URL", | ||||
|     "pick": "Wybierz", | ||||
|     "theme": "Motyw", | ||||
|     "dark": "Ciemny", | ||||
|     "light": "Jasny", | ||||
|     "followSystem": "Zgodny z systemem", | ||||
|     "obtainium": "Obtainium", | ||||
|     "materialYou": "Material You", | ||||
|     "useBlackTheme": "Użyj czarnego motywu", | ||||
|     "appSortBy": "Sortuj aplikacje według", | ||||
|     "authorName": "Autor/Nazwa", | ||||
|     "nameAuthor": "Nazwa/Autor", | ||||
|     "asAdded": "Dodania", | ||||
|     "appSortOrder": "Kolejność sortowania aplikacji", | ||||
|     "ascending": "Rosnąco", | ||||
|     "descending": "Malejąco", | ||||
|     "bgUpdateCheckInterval": "Częstotliwość sprawdzania aktualizacji w tle", | ||||
|     "neverManualOnly": "Nigdy - tylko ręcznie", | ||||
|     "appearance": "Wygląd", | ||||
|     "showWebInAppView": "Pokaż stronę źródłową w widoku aplikacji", | ||||
|     "pinUpdates": "Przypnij aktualizacje na górze widoku aplikacji", | ||||
|     "updates": "Aktualizacje", | ||||
|     "sourceSpecific": "Zależnie od źródła", | ||||
|     "appSource": "Źródło aplikacji", | ||||
|     "noLogs": "Brak logów", | ||||
|     "appLogs": "Logi aplikacji", | ||||
|     "close": "Zamknij", | ||||
|     "share": "Udostępnij", | ||||
|     "appNotFound": "Nie znaleziono aplikacji", | ||||
|     "obtainiumExportHyphenatedLowercase": "obtainium-eksport", | ||||
|     "pickAnAPK": "Wybierz plik APK", | ||||
|     "appHasMoreThanOnePackage": "{} ma więcej niż jeden pakiet:", | ||||
|     "deviceSupportsXArch": "Urządzenie obsługuje architekturę procesora {}.", | ||||
|     "deviceSupportsFollowingArchs": "Urządzenie obsługuje następujące architektury procesora:", | ||||
|     "warning": "Uwaga", | ||||
|     "sourceIsXButPackageFromYPrompt": "Źródłem aplikacji jest '{}', ale pakiet wydania pochodzi z '{}'. Kontynuować?", | ||||
|     "updatesAvailable": "Dostępne aktualizacje", | ||||
|     "updatesAvailableNotifDescription": "Powiadamia użytkownika o dostępności aktualizacji dla jednej lub więcej aplikacji obserwowanych przez Obtainium", | ||||
|     "noNewUpdates": "Brak nowych aktualizacji.", | ||||
|     "xHasAnUpdate": "{} ma aktualizację.", | ||||
|     "appsUpdated": "Zaktualizowane aplikacje", | ||||
|     "appsUpdatedNotifDescription": "Powiadamia użytkownika, gdy jedna lub więcej aplikacji zostało zaktualizowanych w tle", | ||||
|     "xWasUpdatedToY": "{} zaktualizowano do {}.", | ||||
|     "errorCheckingUpdates": "Sprawdzanie błędów aktualizacji", | ||||
|     "errorCheckingUpdatesNotifDescription": "Powiadomienie wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się", | ||||
|     "appsRemoved": "Usunięte aplikacje", | ||||
|     "appsRemovedNotifDescription": "Powiadamia użytkownika, gdy jedna lub więcej aplikacji zostało usuniętych z powodu błędów wczytywania", | ||||
|     "xWasRemovedDueToErrorY": "Usunięto {} z powodu błędu: {}", | ||||
|     "completeAppInstallation": "Ukończenie instalacji aplikacji", | ||||
|     "obtainiumMustBeOpenToInstallApps": "Aby zainstalować aplikacje, Obtainium musi być otwarte", | ||||
|     "completeAppInstallationNotifDescription": "Prosi użytkownika o powrót do Obtainium w celu dokończenia instalacji aplikacji", | ||||
|     "checkingForUpdates": "Sprawdzanie aktualizacji", | ||||
|     "checkingForUpdatesNotifDescription": "Tymczasowe powiadomienie pojawiające się podczas sprawdzania aktualizacji", | ||||
|     "pleaseAllowInstallPerm": "Pozwól Obtainium instalować aplikacje", | ||||
|     "trackOnly": "Tylko obserwuj", | ||||
|     "errorWithHttpStatusCode": "Błąd {}", | ||||
|     "versionCorrectionDisabled": "Korekta wersji wyłączona (wtyczka wydaje się nie działać)", | ||||
|     "unknown": "Nieznane", | ||||
|     "none": "Brak", | ||||
|     "never": "Nigdy", | ||||
|     "latestVersionX": "Najnowsza wersja: {}", | ||||
|     "installedVersionX": "Zainstalowana wersja: {}", | ||||
|     "lastUpdateCheckX": "Ostatnio sprawdzono: {}", | ||||
|     "remove": "Usuń", | ||||
|     "yesMarkUpdated": "Tak, oznacz jako zaktualizowane", | ||||
|     "fdroid": "Oficjalny F-Droid", | ||||
|     "appIdOrName": "ID aplikacji lub nazwa", | ||||
|     "appWithIdOrNameNotFound": "Nie znaleziono aplikacji o tym identyfikatorze lub nazwie", | ||||
|     "reposHaveMultipleApps": "Repozytoria mogą zawierać wiele aplikacji", | ||||
|     "fdroidThirdPartyRepo": "Zewnętrzne repo F-Droid", | ||||
|     "steam": "Steam", | ||||
|     "steamMobile": "Mobilny Steam", | ||||
|     "steamChat": "Steam Chat", | ||||
|     "install": "Instaluj", | ||||
|     "markInstalled": "Oznacz jako zainstalowane", | ||||
|     "update": "Zaktualizuj", | ||||
|     "markUpdated": "Oznacz jako zaktualizowane", | ||||
|     "additionalOptions": "Dodatkowe opcje", | ||||
|     "disableVersionDetection": "Wyłącz wykrywanie wersji", | ||||
|     "noVersionDetectionExplanation": "Opcja ta powinna być używana tylko w przypadku aplikacji, w których wykrywanie wersji nie działa poprawnie.", | ||||
|     "downloadingX": "Pobieranie {}", | ||||
|     "downloadNotifDescription": "Powiadamia użytkownika o postępach w pobieraniu aplikacji", | ||||
|     "noAPKFound": "Nie znaleziono pakietu APK", | ||||
|     "noVersionDetection": "Bez wykrywania wersji", | ||||
|     "categorize": "Kategoryzuj", | ||||
|     "categories": "Kategorie", | ||||
|     "category": "Kategoria", | ||||
|     "noCategory": "Bez kategorii", | ||||
|     "noCategories": "Brak kategorii", | ||||
|     "deleteCategoriesQuestion": "Usunąć kategorie?", | ||||
|     "categoryDeleteWarning": "Wszystkie aplikacje w usuniętych kategoriach zostaną ustawione jako nieskategoryzowane.", | ||||
|     "addCategory": "Dodaj kategorię", | ||||
|     "label": "Etykieta", | ||||
|     "language": "Język", | ||||
|     "copiedToClipboard": "Skopiowano do schowka", | ||||
|     "storagePermissionDenied": "Odmówiono zezwolenia dostępu do pamięci", | ||||
|     "selectedCategorizeWarning": "Spowoduje to zastąpienie wszystkich istniejących ustawień kategorii dla wybranych aplikacji.", | ||||
|     "filterAPKsByRegEx": "Filtruj pliki APK według wyrażeń regularnych", | ||||
|     "removeFromObtainium": "Usuń z Obtainium", | ||||
|     "uninstallFromDevice": "Odinstaluj z urządzenia", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Działa tylko w przypadku aplikacji z wyłączonym wykrywaniem wersji.", | ||||
|     "releaseDateAsVersion": "Użyj daty wydania jako wersji", | ||||
|     "releaseDateAsVersionExplanation": "Opcja ta powinna być używana tylko w przypadku aplikacji, w których wykrywanie wersji nie działa poprawnie, ale dostępna jest data wydania.", | ||||
|     "changes": "Zmiany", | ||||
|     "releaseDate": "Data wydania", | ||||
|     "importFromURLsInFile": "Importuj z adresów URL w pliku (typu OPML)", | ||||
|     "versionDetection": "Wykrywanie wersji", | ||||
|     "standardVersionDetection": "Standardowe wykrywanie wersji", | ||||
|     "groupByCategory": "Grupuj według kategorii", | ||||
|     "autoApkFilterByArch": "Spróbuj filtrować pliki APK według architektury procesora, jeśli to możliwe", | ||||
|     "overrideSource": "Nadpisz źródło", | ||||
|     "dontShowAgain": "Nie pokazuj tego ponownie", | ||||
|     "dontShowTrackOnlyWarnings": "Nie wyświetlaj ostrzeżeń „Tylko obserwowana”", | ||||
|     "dontShowAPKOriginWarnings": "Nie pokazuj ostrzeżeń o pochodzeniu APK", | ||||
|     "moveNonInstalledAppsToBottom": "Przenieś niezainstalowane aplikacje na dół widoku aplikacji", | ||||
|     "gitlabPATLabel": "Osobisty Token Dostępu GitLab (umożliwia wyszukiwanie)", | ||||
|     "about": "Więcej informacji", | ||||
|     "requiresCredentialsInSettings": "Wymaga to dodatkowych poświadczeń (w Ustawieniach)", | ||||
|     "checkOnStart": "Sprawdź raz przy starcie", | ||||
|     "tryInferAppIdFromCode": "Spróbuj wywnioskować identyfikator aplikacji z kodu źródłowego", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Usunąć aplikację?", | ||||
|         "other": "Usunąć aplikacje?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} min.", | ||||
|         "other": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} min." | ||||
|     }, | ||||
|     "bgCheckFoundUpdatesWillNotifyIfNeeded": { | ||||
|         "one": "Podczas sprawdzania aktualizacji w tle znaleziono {} aktualizację - w razie potrzeby użytkownik zostanie o tym powiadomiony", | ||||
|         "other": "Podczas sprawdzania aktualizacji w tle znaleziono {} akt. - w razie potrzeby użytkownik zostanie o tym powiadomiony" | ||||
|     }, | ||||
|     "apps": { | ||||
|         "one": "{} aplik.", | ||||
|         "other": "{} aplik." | ||||
|     }, | ||||
|     "url": { | ||||
|         "one": "{} adres URL", | ||||
|         "other": "{} adr. URL" | ||||
|     }, | ||||
|     "minute": { | ||||
|         "one": "{} min.", | ||||
|         "other": "{} min." | ||||
|     }, | ||||
|     "hour": { | ||||
|         "one": "{} godz.", | ||||
|         "other": "{} godz." | ||||
|     }, | ||||
|     "day": { | ||||
|         "one": "{} dzień", | ||||
|         "other": "{} dni" | ||||
|     }, | ||||
|     "clearedNLogsBeforeXAfterY": { | ||||
|         "one": "Wyczyszczono {n} log (przed = {before}, po = {after})", | ||||
|         "other": "Wyczyszczono logi: {n} (przed = {before}, po = {after})" | ||||
|     }, | ||||
|     "xAndNMoreUpdatesAvailable": { | ||||
|         "one": "{} i jeszcze 1 aplikacja mają aktualizacje.", | ||||
|         "other": "{} i {} aplik. otrzymało aktualizacje." | ||||
|     }, | ||||
|     "xAndNMoreUpdatesInstalled": { | ||||
|         "one": "Zaktualizowano {} i jeszcze 1 aplikację.", | ||||
|         "other": "Zaktualizowano {} i {} aplik." | ||||
|     } | ||||
| } | ||||
							
								
								
									
										285
									
								
								assets/translations/ru.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								assets/translations/ru.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | ||||
| { | ||||
|     "invalidURLForSource": "Неверный URL-адрес {} приложения", | ||||
|     "noReleaseFound": "Не удалось найти подходящий релиз", | ||||
|     "noVersionFound": "Не удалось определить версию релиза", | ||||
|     "urlMatchesNoSource": "URL-адрес не соответствует известному источнику", | ||||
|     "cantInstallOlderVersion": "Невозможно установить более старую версию приложения", | ||||
|     "appIdMismatch": "ID загруженного пакета не совпадает с существующим ID приложения", | ||||
|     "functionNotImplemented": "Этот класс не реализовал эту функцию", | ||||
|     "placeholder": "Заполнитель", | ||||
|     "someErrors": "Произошли некоторые ошибки", | ||||
|     "unexpectedError": "Неожиданная ошибка", | ||||
|     "ok": "Окей", | ||||
|     "and": "и", | ||||
|     "startedBgUpdateTask": "Запущена задача фоновой проверки обновлений", | ||||
|     "bgUpdateIgnoreAfterIs": "Параметр игнорирования фоновых обновлений: {}", | ||||
|     "startedActualBGUpdateCheck": "Запущена фактическая проверка фоновых обновлений", | ||||
|     "bgUpdateTaskFinished": "Завершена задача фоновой проверки обновлений", | ||||
|     "firstRun": "Это первый запуск Obtainium", | ||||
|     "settingUpdateCheckIntervalTo": "Установка интервала проверки обновлений: {}", | ||||
|     "githubPATLabel": "Персональный токен доступа GitHub (увеличивает лимит запросов)", | ||||
|     "githubPATHint": "Токен доступа должен быть в формате: имя_пользователя:токен", | ||||
|     "githubPATFormat": "имя_пользователя:токен", | ||||
|     "includePrereleases": "Включить предварительные релизы", | ||||
|     "fallbackToOlderReleases": "Откатиться к более старым версиям", | ||||
|     "filterReleaseTitlesByRegEx": "Фильтровать заголовки релизов с помощью регулярного выражения", | ||||
|     "invalidRegEx": "Неверное регулярное выражение", | ||||
|     "noDescription": "Нет описания", | ||||
|     "cancel": "Отмена", | ||||
|     "continue": "Продолжить", | ||||
|     "requiredInBrackets": "(Обязательно)", | ||||
|     "dropdownNoOptsError": "Ошибка: Выпадающий список должен содержать хотя бы одну опцию", | ||||
|     "colour": "Цвет", | ||||
|     "githubStarredRepos": "Помеченные звездочкой репозитории на GitHub", | ||||
|     "uname": "Имя пользователя", | ||||
|     "wrongArgNum": "Неправильное количество предоставленных аргументов", | ||||
|     "xIsTrackOnly": "{} является приложением только для отслеживания", | ||||
|     "source": "Источник", | ||||
|     "app": "Приложение", | ||||
|     "appsFromSourceAreTrackOnly": "Приложения из этого источника являются 'только для отслеживания'.", | ||||
|     "youPickedTrackOnly": "Вы выбрали опцию 'Только для отслеживания'.", | ||||
|     "trackOnlyAppDescription": "Приложение будет отслеживаться на предмет обновлений, но Obtainium не сможет загрузить или установить его.", | ||||
|     "cancelled": "Отменено", | ||||
|     "appAlreadyAdded": "Приложение уже добавлено", | ||||
|     "alreadyUpToDateQuestion": "Приложение уже обновлено?", | ||||
|     "addApp": "Добавить приложение", | ||||
|     "appSourceURL": "URL-источник приложения", | ||||
|     "error": "Ошибка", | ||||
|     "add": "Добавить", | ||||
|     "searchSomeSourcesLabel": "Поиск (только в некоторых источниках)", | ||||
|     "search": "Поиск", | ||||
|     "additionalOptsFor": "Дополнительные опции для {}", | ||||
|     "supportedSourcesBelow": "Поддерживаемые источники:", | ||||
|     "trackOnlyInBrackets": "(Только для отслеживания)", | ||||
|     "searchableInBrackets": "(Поиск)", | ||||
|     "appsString": "Приложения", | ||||
|     "noApps": "Нет приложений", | ||||
|     "noAppsForFilter": "Нет приложений для фильтра", | ||||
|     "byX": "От {}", | ||||
|     "percentProgress": "Прогресс: {}%", | ||||
|     "pleaseWait": "Пожалуйста, подождите", | ||||
|     "updateAvailable": "Доступно обновление", | ||||
|     "estimateInBracketsShort": "(Оценка)", | ||||
|     "notInstalled": "Не установлено", | ||||
|     "estimateInBrackets": "(Оценка)", | ||||
|     "selectAll": "Выбрать все", | ||||
|     "deselectN": "Отменить выбор {}", | ||||
|     "xWillBeRemovedButRemainInstalled": "{} будет удалено из Obtainium, но останется установленным на устройстве.", | ||||
|     "removeSelectedAppsQuestion": "Удалить выбранные приложения?", | ||||
|     "removeSelectedApps": "Удалить выбранные приложения", | ||||
|     "updateX": "Обновить {}", | ||||
|     "installX": "Установить {}", | ||||
|     "markXTrackOnlyAsUpdated": "Отметить {}\n(Только для отслеживания)\nкак обновленное", | ||||
|     "changeX": "Изменить {}", | ||||
|     "installUpdateApps": "Установить/Обновить приложения", | ||||
|     "installUpdateSelectedApps": "Установить/Обновить выбранные приложения", | ||||
|     "markXSelectedAppsAsUpdated": "Отметить {} выбранные приложения как обновленные?", | ||||
|     "no": "Нет", | ||||
|     "yes": "Да", | ||||
|     "markSelectedAppsUpdated": "Отметить выбранные приложения как обновленные", | ||||
|     "pinToTop": "Закрепить сверху", | ||||
|     "unpinFromTop": "Открепить", | ||||
|     "resetInstallStatusForSelectedAppsQuestion": "Сбросить статус установки для выбранных приложений?", | ||||
|     "installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, неправильная из-за неудачных обновлений или других проблем.", | ||||
|     "shareSelectedAppURLs": "Поделиться выбранными URL-адресами приложений", | ||||
|     "resetInstallStatus": "Сбросить статус установки", | ||||
|     "more": "Еще", | ||||
|     "removeOutdatedFilter": "Удалить фильтр для устаревших приложений", | ||||
|     "showOutdatedOnly": "Показывать только устаревшие приложения", | ||||
|     "filter": "Фильтр", | ||||
|     "filterActive": "Фильтр *", | ||||
|     "filterApps": "Фильтровать приложения", | ||||
|     "appName": "Название приложения", | ||||
|     "author": "Автор", | ||||
|     "upToDateApps": "Приложения со свежими обновлениями", | ||||
|     "nonInstalledApps": "Неустановленные приложения", | ||||
|     "importExport": "Импорт/экспорт", | ||||
|     "settings": "Настройки", | ||||
|     "exportedTo": "Экспортировано в {}", | ||||
|     "obtainiumExport": "Экспорт из Obtainium", | ||||
|     "invalidInput": "Неверный ввод", | ||||
|     "importedX": "Импортировано {}", | ||||
|     "obtainiumImport": "Импорт в Obtainium", | ||||
|     "importFromURLList": "Импорт из списка URL-адреса", | ||||
|     "searchQuery": "Поисковый запрос", | ||||
|     "appURLList": "Список URL приложений", | ||||
|     "line": "Строка", | ||||
|     "searchX": "Поиск {}", | ||||
|     "noResults": "Результатов не найдено", | ||||
|     "importX": "Импорт {}", | ||||
|     "importedAppsIdDisclaimer": "Импортированные приложения могут неверно отображаться как 'Не установлены'.\nДля исправления этой проблемы повторно установите их через Obtainium.\nЭто не должно повлиять на данные приложения.\n\nПроблемы возникают только при импорте из URL-адреса и сторонних источников.", | ||||
|     "importErrors": "Ошибка импорта", | ||||
|     "importedXOfYApps": "Импортировано {} из {} приложений.", | ||||
|     "followingURLsHadErrors": "При импорте следующие URL-адреса содержали ошибки:", | ||||
|     "okay": "Окей", | ||||
|     "selectURL": "Выбрать URL-адрес", | ||||
|     "selectURLs": "Выбрать URL-адреса", | ||||
|     "pick": "Выбрать", | ||||
|     "theme": "Тема", | ||||
|     "dark": "Темный", | ||||
|     "light": "Светлый", | ||||
|     "followSystem": "Следовать системе", | ||||
|     "obtainium": "Obtainium", | ||||
|     "materialYou": "Material You", | ||||
|     "useBlackTheme": "Использовать темную тему", | ||||
|     "appSortBy": "Сортировка приложений по", | ||||
|     "authorName": "Автор/Название", | ||||
|     "nameAuthor": "Название/Автор", | ||||
|     "asAdded": "В порядке добавления", | ||||
|     "appSortOrder": "Порядок сортировки приложений", | ||||
|     "ascending": "По возрастанию", | ||||
|     "descending": "По убыванию", | ||||
|     "bgUpdateCheckInterval": "Интервал проверки обновлений в фоновом режиме", | ||||
|     "neverManualOnly": "Никогда - Только вручную", | ||||
|     "appearance": "Внешний вид", | ||||
|     "showWebInAppView": "Показывать веб-страницу источника в представлении приложения", | ||||
|     "pinUpdates": "Закрепить обновления сверху списка приложений", | ||||
|     "updates": "Обновления", | ||||
|     "sourceSpecific": "Специфика источника", | ||||
|     "appSource": "Источник приложения", | ||||
|     "noLogs": "Нет журналов", | ||||
|     "appLogs": "Журналы приложений", | ||||
|     "close": "Закрыть", | ||||
|     "share": "Поделиться", | ||||
|     "appNotFound": "Приложение не найдено", | ||||
|     "obtainiumExportHyphenatedLowercase": "obtainium-export", | ||||
|     "pickAnAPK": "Выберите APK-файл", | ||||
|     "appHasMoreThanOnePackage": "{} имеет более одного пакета:", | ||||
|     "deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}.", | ||||
|     "deviceSupportsFollowingArchs": "Ваше устройство поддерживает следующие архитектуры процессора:", | ||||
|     "warning": "Предупреждение", | ||||
|     "sourceIsXButPackageFromYPrompt": "Источник приложения - '{}', но пакет для установки получен из '{}'. Продолжить?", | ||||
|     "updatesAvailable": "Доступны обновления", | ||||
|     "updatesAvailableNotifDescription": "Уведомляет пользователя о наличии обновлений для одного или нескольких приложений, отслеживаемых Obtainium", | ||||
|     "noNewUpdates": "Нет новых обновлений.", | ||||
|     "xHasAnUpdate": "{} есть обновление.", | ||||
|     "appsUpdated": "Приложения обновлены", | ||||
|     "appsUpdatedNotifDescription": "Уведомляет пользователя о том, что обновления для одного или нескольких приложений были применены в фоновом режиме", | ||||
|     "xWasUpdatedToY": "{} была обновлена до версии {}.", | ||||
|     "errorCheckingUpdates": "Ошибка при проверке обновлений", | ||||
|     "errorCheckingUpdatesNotifDescription": "Уведомление, которое появляется, когда проверка обновлений в фоновом режиме завершилась с ошибкой", | ||||
|     "appsRemoved": "Приложение удалено", | ||||
|     "appsRemovedNotifDescription": "Уведомляет пользователя о том, что одно или несколько приложений было удалено из-за ошибок при их загрузке", | ||||
|     "xWasRemovedDueToErrorY": "{} был удален из-за ошибки: {}", | ||||
|     "completeAppInstallation": "Завершение установки приложения", | ||||
|     "obtainiumMustBeOpenToInstallApps": "Для установки приложений Obtainium должен быть открыт", | ||||
|     "completeAppInstallationNotifDescription": "Просит пользователя вернуться в Obtainium, чтобы завершить установку приложения", | ||||
|     "checkingForUpdates": "Проверка обновлений", | ||||
|     "checkingForUpdatesNotifDescription": "Временное уведомление, которое появляется при проверке обновлений", | ||||
|     "pleaseAllowInstallPerm": "Пожалуйста, разрешите Obtainium устанавливать приложения", | ||||
|     "trackOnly": "Только отслеживать", | ||||
|     "errorWithHttpStatusCode": "Ошибка {}", | ||||
|     "versionCorrectionDisabled": "Коррекция версий отключена (плагин, кажется, не работает)", | ||||
|     "unknown": "Неизвестно", | ||||
|     "none": "Отсутствует", | ||||
|     "never": "Никогда", | ||||
|     "latestVersionX": "Последняя версия: {}", | ||||
|     "installedVersionX": "Установленная версия: {}", | ||||
|     "lastUpdateCheckX": "Последняя проверка обновлений: {}", | ||||
|     "remove": "Удалить", | ||||
|     "yesMarkUpdated": "Да, отметить как обновленное", | ||||
|     "fdroid": "Официальный F-Droid", | ||||
|     "appIdOrName": "ID или название приложения", | ||||
|     "appId": "ID приложения", | ||||
|     "appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено", | ||||
|     "reposHaveMultipleApps": "В хранилище может быть несколько приложений", | ||||
|     "fdroidThirdPartyRepo": "Хранилище F-Droid сторонних разработчиков", | ||||
|     "steam": "Steam", | ||||
|     "steamMobile": "Steam Mobile", | ||||
|     "steamChat": "Steam Chat", | ||||
|     "install": "Установить", | ||||
|     "markInstalled": "Пометить как установленное", | ||||
|     "update": "Обновить", | ||||
|     "markUpdated": "Отметить обновленным", | ||||
|     "additionalOptions": "Дополнительные опции", | ||||
|     "disableVersionDetection": "Отключить обнаружение версии", | ||||
|     "noVersionDetectionExplanation": "Эта опция должна использоваться только для приложений, где обнаружение версии не работает корректно.", | ||||
|     "downloadingX": "Загрузка {}", | ||||
|     "downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения", | ||||
|     "noAPKFound": "APK не найден", | ||||
|     "noVersionDetection": "Версий не обнаружено", | ||||
|     "categorize": "Категоризировать", | ||||
|     "categories": "Категории", | ||||
|     "category": "Категория", | ||||
|     "noCategory": "Без категории", | ||||
|     "noCategories": "Без категорий", | ||||
|     "deleteCategoriesQuestion": "Удалить категории?", | ||||
|     "categoryDeleteWarning": "Все приложения в удаленных категориях будут помечены как без категории.", | ||||
|     "addCategory": "Добавить категорию", | ||||
|     "label": "Метка", | ||||
|     "language": "Язык", | ||||
|     "copiedToClipboard": "Скопировано в буфер обмена", | ||||
|     "storagePermissionDenied": "Отказано в доступе к хранилищу", | ||||
|     "selectedCategorizeWarning": "Это заменит все текущие настройки категорий для выбранных приложений.", | ||||
|     "filterAPKsByRegEx": "Фильтровать APK-файлы с помощью регулярного выражения", | ||||
|     "removeFromObtainium": "Удалить из Obtainium", | ||||
|     "uninstallFromDevice": "Удалить с устройства", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Работает только для приложений с отключенным определением версии.", | ||||
|     "releaseDateAsVersion": "Использовать дату выпуска в качестве версии", | ||||
|     "releaseDateAsVersionExplanation": "Этот параметр следует использовать только для приложений, в которых определение версии не работает правильно, но имеется дата выпуска.", | ||||
|     "changes": "Изменения", | ||||
|     "releaseDate": "Дата выпуска", | ||||
|     "importFromURLsInFile": "Импортировать из URL-адресов в файл (например, OPML)", | ||||
|     "versionDetection": "Определение версии", | ||||
|     "standardVersionDetection": "Стандартное определение версии", | ||||
|     "groupByCategory": "Группировать по категориям", | ||||
|     "autoApkFilterByArch": "Попытка фильтрации APK-файлов по архитектуре процессора, если это возможно", | ||||
|     "overrideSource": "Переопределить источник", | ||||
|     "dontShowAgain": "Не показывать снова", | ||||
|     "dontShowTrackOnlyWarnings": "Не показывать предупреждения о только отслеживаемых приложениях", | ||||
|     "dontShowAPKOriginWarnings": "Не показывать предупреждения об источнике APK-файлов", | ||||
|     "moveNonInstalledAppsToBottom": "Переместить неустановленные приложения вниз списка", | ||||
|     "gitlabPATLabel": "Персональный токен доступа GitLab (Включает поиск)", | ||||
|     "about": "О приложении", | ||||
|     "requiresCredentialsInSettings": "Для этого требуются дополнительные учетные данные (в настройках)", | ||||
|     "checkOnStart": "Проверить один раз при запуске", | ||||
|     "tryInferAppIdFromCode": "Попытаться определить ID приложения из исходного кода", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Удалить приложение?", | ||||
|         "other": "Удалить приложения?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Слишком много запросов (ограничение скорости) - попробуйте снова через {} минуту", | ||||
|         "other": "Слишком много запросов (ограничение скорости) - попробуйте снова через {} минуты" | ||||
|     }, | ||||
|     "bgUpdateGotErrorRetryInMinutes": { | ||||
|         "one": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуту", | ||||
|         "other": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуты" | ||||
|     }, | ||||
|     "bgCheckFoundUpdatesWillNotifyIfNeeded": { | ||||
|         "one": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновление - Пользователю будет отправлено уведомление, если это необходимо", | ||||
|         "other": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновлений - Пользователю будет отправлено уведомление, если это необходимо" | ||||
|     }, | ||||
|     "apps": { | ||||
|         "one": "{} Приложение", | ||||
|         "other": "{} Приложений" | ||||
|     }, | ||||
|     "url": { | ||||
|         "one": "{} Ссылка", | ||||
|         "other": "{} Ссылки" | ||||
|     }, | ||||
|     "minute": { | ||||
|         "one": "{} Минута", | ||||
|         "other": "{} Минуты" | ||||
|     }, | ||||
|     "hour": { | ||||
|         "one": "{} Час", | ||||
|         "other": "{} Часов" | ||||
|     }, | ||||
|     "day": { | ||||
|         "one": "{} День", | ||||
|         "other": "{} Дней" | ||||
|     }, | ||||
|     "clearedNLogsBeforeXAfterY": { | ||||
|         "one": "Очищен {n} журнал (до = {before}, после = {after})", | ||||
|         "other": "Очищено {n} журналов (до = {before}, после = {after})" | ||||
|     }, | ||||
|     "xAndNMoreUpdatesAvailable": { | ||||
|         "one": "У {} и еще 1 приложения есть обновления.", | ||||
|         "other": "{} and {} more apps have updates." | ||||
|     }, | ||||
|     "xAndNMoreUpdatesInstalled": { | ||||
|         "one": "{} and 1 more app were updated.", | ||||
|         "other": "У {} и еще {} приложений есть обновления." | ||||
|     } | ||||
| } | ||||
| @@ -33,7 +33,7 @@ | ||||
|     "githubStarredRepos": "GitHub 已星标仓库", | ||||
|     "uname": "用户名", | ||||
|     "wrongArgNum": "参数数量错误", | ||||
|     "xIsTrackOnly": "{} 为“仅追踪”模式", | ||||
|     "xIsTrackOnly": "{}为“仅追踪”模式", | ||||
|     "source": "源代码", | ||||
|     "app": "应用", | ||||
|     "appsFromSourceAreTrackOnly": "此来源的应用为“仅追踪”模式。", | ||||
| @@ -50,8 +50,8 @@ | ||||
|     "search": "搜索", | ||||
|     "additionalOptsFor": "{} 的更多选项", | ||||
|     "supportedSourcesBelow": "支持的来源:", | ||||
|     "trackOnlyInBrackets": "(仅追踪)", | ||||
|     "searchableInBrackets": "(可搜索)", | ||||
|     "trackOnlyInBrackets": "(仅追踪)", | ||||
|     "searchableInBrackets": "(可搜索)", | ||||
|     "appsString": "应用列表", | ||||
|     "noApps": "无应用", | ||||
|     "noAppsForFilter": "没有符合条件的应用", | ||||
| @@ -59,9 +59,9 @@ | ||||
|     "percentProgress": "进度:{}%", | ||||
|     "pleaseWait": "请稍候", | ||||
|     "updateAvailable": "更新可用", | ||||
|     "estimateInBracketsShort": "(预计)", | ||||
|     "estimateInBracketsShort": "(推测)", | ||||
|     "notInstalled": "未安装", | ||||
|     "estimateInBrackets": "(预计)", | ||||
|     "estimateInBrackets": "(推测)", | ||||
|     "selectAll": "全选", | ||||
|     "deselectN": "取消选择 {}", | ||||
|     "xWillBeRemovedButRemainInstalled": "{} 将从 Obtainium 中删除,但仍安装在您的设备中。", | ||||
| @@ -74,8 +74,8 @@ | ||||
|     "installUpdateApps": "安装/更新应用", | ||||
|     "installUpdateSelectedApps": "安装/更新选中的应用", | ||||
|     "markXSelectedAppsAsUpdated": "是否将选中的 {} 个应用标记为已更新?", | ||||
|     "no": "不要", | ||||
|     "yes": "好的", | ||||
|     "no": "否", | ||||
|     "yes": "是", | ||||
|     "markSelectedAppsUpdated": "将选中的应用标记为已更新", | ||||
|     "pinToTop": "置顶", | ||||
|     "unpinFromTop": "取消置顶", | ||||
| @@ -142,7 +142,7 @@ | ||||
|     "close": "关闭", | ||||
|     "share": "分享", | ||||
|     "appNotFound": "未找到应用", | ||||
|     "obtainiumExportHyphenatedLowercase": "obtainium-导出", | ||||
|     "obtainiumExportHyphenatedLowercase": "obtainium-export", | ||||
|     "pickAnAPK": "选择一个 APK 文件", | ||||
|     "appHasMoreThanOnePackage": "{} 有多个架构可用:", | ||||
|     "deviceSupportsXArch": "您的设备支持 {} 架构。", | ||||
| @@ -172,14 +172,15 @@ | ||||
|     "versionCorrectionDisabled": "禁用版本号更正(插件似乎未起作用)", | ||||
|     "unknown": "未知", | ||||
|     "none": "无", | ||||
|     "never": "从不", | ||||
|     "never": "从未", | ||||
|     "latestVersionX": "最新版本:{}", | ||||
|     "installedVersionX": "当前版本:{}", | ||||
|     "lastUpdateCheckX": "上次更新检查:{}", | ||||
|     "remove": "删除", | ||||
|     "yesMarkUpdated": "是的,标记为已更新", | ||||
|     "fdroid": "F-Droid Official", | ||||
|     "yesMarkUpdated": "是,标记为已更新", | ||||
|     "fdroid": "F-Droid 官方存储库", | ||||
|     "appIdOrName": "应用 ID 或名称", | ||||
|     "appId": "App ID", | ||||
|     "appWithIdOrNameNotFound": "未找到符合此 ID 或名称的应用", | ||||
|     "reposHaveMultipleApps": "存储库中可能包含多个应用", | ||||
|     "fdroidThirdPartyRepo": "F-Droid 第三方存储库", | ||||
| @@ -193,7 +194,7 @@ | ||||
|     "additionalOptions": "附加选项", | ||||
|     "disableVersionDetection": "禁用版本检测", | ||||
|     "noVersionDetectionExplanation": "此选项应该仅用于无法进行版本检测的应用。", | ||||
|     "downloadingX": "正在下载 {}", | ||||
|     "downloadingX": "正在下载{}", | ||||
|     "downloadNotifDescription": "提示应用的下载进度", | ||||
|     "noAPKFound": "未找到 APK 文件", | ||||
|     "noVersionDetection": "禁用版本检测", | ||||
| @@ -222,16 +223,17 @@ | ||||
|     "versionDetection": "版本检测", | ||||
|     "standardVersionDetection": "常规版本检测", | ||||
|     "groupByCategory": "按类别分组显示", | ||||
|     "autoApkFilterByArch": "如果可能,尝试按 CPU 架构筛选 APK 文件", | ||||
|     "overrideSource": "Override Source", | ||||
|     "dontShowAgain": "Don't show this again", | ||||
|     "dontShowTrackOnlyWarnings": "Don't Show the 'Track-Only' Warning", | ||||
|     "dontShowAPKOriginWarnings": "Don't Show APK Origin Warnings", | ||||
|     "moveNonInstalledAppsToBottom": "Move Non-Installed Apps to Bottom of Apps View", | ||||
|     "gitlabPATLabel": "GitLab Personal Access Token (Enables Search)", | ||||
|     "about": "About", | ||||
|     "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", | ||||
|     "checkOnStart": "Check Once on Start", | ||||
|     "autoApkFilterByArch": "如果可能,尝试按设备支持的 CPU 架构筛选 APK 文件", | ||||
|     "overrideSource": "覆盖来源", | ||||
|     "dontShowAgain": "不再显示", | ||||
|     "dontShowTrackOnlyWarnings": "不显示“仅追踪”模式警告", | ||||
|     "dontShowAPKOriginWarnings": "不显示 APK 文件来源警告", | ||||
|     "moveNonInstalledAppsToBottom": "将未安装应用置底", | ||||
|     "gitlabPATLabel": "GitLab 个人访问令牌(用于搜索)", | ||||
|     "about": "相关文档", | ||||
|     "requiresCredentialsInSettings": "此功能需要额外的凭据(在“设置”中添加)", | ||||
|     "checkOnStart": "启动时进行一次检查", | ||||
|     "tryInferAppIdFromCode": "Try inferring App ID from source code", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "是否删除应用?", | ||||
|         "other": "是否删除应用?" | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:html/parser.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
|  | ||||
| @@ -20,8 +19,8 @@ class APKCombo extends AppSource { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   String? tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) { | ||||
|   Future<String?> tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) async { | ||||
|     return Uri.parse(standardUrl).pathSegments.last; | ||||
|   } | ||||
|  | ||||
| @@ -84,8 +83,7 @@ class APKCombo extends AppSource { | ||||
|     String standardUrl, | ||||
|     Map<String, dynamic> additionalSettings, | ||||
|   ) async { | ||||
|     String appId = tryInferringAppId(standardUrl)!; | ||||
|     String host = Uri.parse(standardUrl).host; | ||||
|     String appId = (await tryInferringAppId(standardUrl))!; | ||||
|     var preres = await sourceRequest(standardUrl); | ||||
|     if (preres.statusCode != 200) { | ||||
|       throw getObtainiumHttpError(preres); | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:html/parser.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
|  | ||||
| @@ -25,8 +24,8 @@ class APKPure extends AppSource { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   String? tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) { | ||||
|   Future<String?> tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) async { | ||||
|     return Uri.parse(standardUrl).pathSegments.last; | ||||
|   } | ||||
|  | ||||
| @@ -35,7 +34,7 @@ class APKPure extends AppSource { | ||||
|     String standardUrl, | ||||
|     Map<String, dynamic> additionalSettings, | ||||
|   ) async { | ||||
|     String appId = tryInferringAppId(standardUrl)!; | ||||
|     String appId = (await tryInferringAppId(standardUrl))!; | ||||
|     String host = Uri.parse(standardUrl).host; | ||||
|     var res = await sourceRequest('$standardUrl/download'); | ||||
|     if (res.statusCode == 200) { | ||||
|   | ||||
| @@ -1,6 +1,4 @@ | ||||
| import 'dart:convert'; | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/app_sources/github.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| @@ -9,7 +7,6 @@ import 'package:obtainium/providers/source_provider.dart'; | ||||
| class Codeberg extends AppSource { | ||||
|   Codeberg() { | ||||
|     host = 'codeberg.org'; | ||||
|     overrideEligible = true; | ||||
|  | ||||
|     additionalSourceSpecificSettingFormItems = []; | ||||
|  | ||||
| @@ -58,10 +55,10 @@ class Codeberg extends AppSource { | ||||
|     String standardUrl, | ||||
|     Map<String, dynamic> additionalSettings, | ||||
|   ) async { | ||||
|     return gh.getLatestAPKDetailsCommon( | ||||
|         'https://$host/api/v1/repos${standardUrl.substring('https://$host'.length)}/releases?per_page=100', | ||||
|         standardUrl, | ||||
|         additionalSettings); | ||||
|     return await gh.getLatestAPKDetailsCommon2(standardUrl, additionalSettings, | ||||
|         (bool useTagUrl) async { | ||||
|       return 'https://$host/api/v1/repos${standardUrl.substring('https://$host'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100'; | ||||
|     }, null); | ||||
|   } | ||||
|  | ||||
|   AppNames getAppNames(String standardUrl) { | ||||
|   | ||||
| @@ -31,8 +31,8 @@ class FDroid extends AppSource { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   String? tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) { | ||||
|   Future<String?> tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) async { | ||||
|     return Uri.parse(standardUrl).pathSegments.last; | ||||
|   } | ||||
|  | ||||
| @@ -63,7 +63,7 @@ class FDroid extends AppSource { | ||||
|     String standardUrl, | ||||
|     Map<String, dynamic> additionalSettings, | ||||
|   ) async { | ||||
|     String? appId = tryInferringAppId(standardUrl); | ||||
|     String? appId = await tryInferringAppId(standardUrl); | ||||
|     String host = Uri.parse(standardUrl).host; | ||||
|     return getAPKUrlsFromFDroidPackagesAPIResponse( | ||||
|         await sourceRequest('https://$host/api/v1/packages/$appId'), | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:html/parser.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
| @@ -8,7 +7,6 @@ import 'package:obtainium/providers/source_provider.dart'; | ||||
| class FDroidRepo extends AppSource { | ||||
|   FDroidRepo() { | ||||
|     name = tr('fdroidThirdPartyRepo'); | ||||
|     overrideEligible = true; | ||||
|  | ||||
|     additionalSourceAppSpecificSettingFormItems = [ | ||||
|       [ | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import 'package:obtainium/app_sources/html.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/apps_provider.dart'; | ||||
| import 'package:obtainium/providers/logs_provider.dart'; | ||||
| import 'package:obtainium/providers/settings_provider.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
| import 'package:url_launcher/url_launcher_string.dart'; | ||||
| @@ -13,7 +14,7 @@ import 'package:url_launcher/url_launcher_string.dart'; | ||||
| class GitHub extends AppSource { | ||||
|   GitHub() { | ||||
|     host = 'github.com'; | ||||
|     overrideEligible = true; | ||||
|     appIdInferIsOptional = true; | ||||
|  | ||||
|     additionalSourceSpecificSettingFormItems = [ | ||||
|       GeneratedFormTextField('github-creds', | ||||
| @@ -80,6 +81,55 @@ class GitHub extends AppSource { | ||||
|     canSearch = true; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Future<String?> tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) async { | ||||
|     const possibleBuildGradleLocations = [ | ||||
|       '/app/build.gradle', | ||||
|       'android/app/build.gradle', | ||||
|       'src/app/build.gradle' | ||||
|     ]; | ||||
|     for (var path in possibleBuildGradleLocations) { | ||||
|       try { | ||||
|         var res = await sourceRequest( | ||||
|             '${await convertStandardUrlToAPIUrl(standardUrl)}/contents/$path'); | ||||
|         if (res.statusCode == 200) { | ||||
|           try { | ||||
|             var body = jsonDecode(res.body); | ||||
|             var trimmedLines = utf8 | ||||
|                 .decode(base64 | ||||
|                     .decode(body['content'].toString().split('\n').join(''))) | ||||
|                 .split('\n') | ||||
|                 .map((e) => e.trim()); | ||||
|             var appId = trimmedLines | ||||
|                 .where((l) => | ||||
|                     l.startsWith('applicationId "') || | ||||
|                     l.startsWith('applicationId \'')) | ||||
|                 .first; | ||||
|             appId = appId | ||||
|                 .split(appId.startsWith('applicationId "') ? '"' : '\'')[1]; | ||||
|             if (appId.startsWith('\${') && appId.endsWith('}')) { | ||||
|               appId = trimmedLines | ||||
|                   .where((l) => l.startsWith( | ||||
|                       'def ${appId.substring(2, appId.length - 1)}')) | ||||
|                   .first; | ||||
|               appId = appId.split(appId.contains('"') ? '"' : '\'')[1]; | ||||
|             } | ||||
|             if (appId.isNotEmpty) { | ||||
|               return appId; | ||||
|             } | ||||
|           } catch (err) { | ||||
|             LogsProvider().add( | ||||
|                 'Error parsing build.gradle from ${res.request!.url.toString()}: ${err.toString()}'); | ||||
|           } | ||||
|         } | ||||
|       } catch (err) { | ||||
|         // Ignore - ID will be extracted from the APK | ||||
|       } | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   String sourceSpecificStandardizeURL(String url) { | ||||
|     RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+'); | ||||
| @@ -98,6 +148,12 @@ class GitHub extends AppSource { | ||||
|     return creds != null && creds.isNotEmpty ? '$creds@' : ''; | ||||
|   } | ||||
|  | ||||
|   Future<String> getAPIHost() async => | ||||
|       'https://${await getCredentialPrefixIfAny()}api.$host'; | ||||
|  | ||||
|   Future<String> convertStandardUrlToAPIUrl(String standardUrl) async => | ||||
|       '${await getAPIHost()}/repos${standardUrl.substring('https://$host'.length)}'; | ||||
|  | ||||
|   @override | ||||
|   String? changeLogPageFromStandardUrl(String standardUrl) => | ||||
|       '$standardUrl/releases'; | ||||
| @@ -143,15 +199,17 @@ class GitHub extends AppSource { | ||||
|         } else if (b == null) { | ||||
|           return 1; | ||||
|         } else { | ||||
|           var stdFormats = findStandardFormatsForVersion(a['tag_name'], true) | ||||
|               .intersection(findStandardFormatsForVersion(b['tag_name'], true)); | ||||
|           var nameA = a['tag_name'] ?? a['name']; | ||||
|           var nameB = b['tag_name'] ?? b['name']; | ||||
|           var stdFormats = findStandardFormatsForVersion(nameA, true) | ||||
|               .intersection(findStandardFormatsForVersion(nameB, true)); | ||||
|           if (stdFormats.isNotEmpty) { | ||||
|             var reg = RegExp(stdFormats.first); | ||||
|             var matchA = reg.firstMatch(a['tag_name']); | ||||
|             var matchB = reg.firstMatch(b['tag_name']); | ||||
|             var matchA = reg.firstMatch(nameA); | ||||
|             var matchB = reg.firstMatch(nameB); | ||||
|             return compareAlphaNumeric( | ||||
|                 (a['tag_name'] as String).substring(matchA!.start, matchA.end), | ||||
|                 (b['tag_name'] as String).substring(matchB!.start, matchB.end)); | ||||
|                 (nameA as String).substring(matchA!.start, matchA.end), | ||||
|                 (nameB as String).substring(matchB!.start, matchB.end)); | ||||
|           } else { | ||||
|             return getReleaseDateFromRelease(a)! | ||||
|                 .compareTo(getReleaseDateFromRelease(b)!); | ||||
| @@ -191,7 +249,7 @@ class GitHub extends AppSource { | ||||
|       if (targetRelease == null) { | ||||
|         throw NoReleasesError(); | ||||
|       } | ||||
|       String? version = targetRelease['tag_name']; | ||||
|       String? version = targetRelease['tag_name'] ?? targetRelease['name']; | ||||
|       DateTime? releaseDate = getReleaseDateFromRelease(targetRelease); | ||||
|       if (version == null) { | ||||
|         throw NoVersionError(); | ||||
| @@ -211,15 +269,35 @@ class GitHub extends AppSource { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   getLatestAPKDetailsCommon2( | ||||
|       String standardUrl, | ||||
|       Map<String, dynamic> additionalSettings, | ||||
|       Future<String> Function(bool) reqUrlGenerator, | ||||
|       dynamic Function(Response)? onHttpErrorCode) async { | ||||
|     try { | ||||
|       return await getLatestAPKDetailsCommon( | ||||
|           await reqUrlGenerator(false), standardUrl, additionalSettings, | ||||
|           onHttpErrorCode: onHttpErrorCode); | ||||
|     } catch (err) { | ||||
|       if (err is NoReleasesError && additionalSettings['trackOnly'] == true) { | ||||
|         return await getLatestAPKDetailsCommon( | ||||
|             await reqUrlGenerator(true), standardUrl, additionalSettings, | ||||
|             onHttpErrorCode: onHttpErrorCode); | ||||
|       } else { | ||||
|         rethrow; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Future<APKDetails> getLatestAPKDetails( | ||||
|     String standardUrl, | ||||
|     Map<String, dynamic> additionalSettings, | ||||
|   ) async { | ||||
|     return getLatestAPKDetailsCommon( | ||||
|         'https://${await getCredentialPrefixIfAny()}api.$host/repos${standardUrl.substring('https://$host'.length)}/releases?per_page=100', | ||||
|         standardUrl, | ||||
|         additionalSettings, onHttpErrorCode: (Response res) { | ||||
|     return await getLatestAPKDetailsCommon2(standardUrl, additionalSettings, | ||||
|         (bool useTagUrl) async { | ||||
|       return '${await convertStandardUrlToAPIUrl(standardUrl)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100'; | ||||
|     }, (Response res) { | ||||
|       rateLimitErrorCheck(res); | ||||
|     }); | ||||
|   } | ||||
| @@ -260,7 +338,7 @@ class GitHub extends AppSource { | ||||
|   Future<Map<String, List<String>>> search(String query) async { | ||||
|     return searchCommon( | ||||
|         query, | ||||
|         'https://${await getCredentialPrefixIfAny()}api.$host/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100', | ||||
|         '${await getAPIHost()}/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100', | ||||
|         'items', onHttpErrorCode: (Response res) { | ||||
|       rateLimitErrorCheck(res); | ||||
|     }); | ||||
|   | ||||
| @@ -14,7 +14,6 @@ import 'package:url_launcher/url_launcher_string.dart'; | ||||
| class GitLab extends AppSource { | ||||
|   GitLab() { | ||||
|     host = 'gitlab.com'; | ||||
|     overrideEligible = true; | ||||
|     canSearch = true; | ||||
|  | ||||
|     additionalSourceSpecificSettingFormItems = [ | ||||
| @@ -83,12 +82,12 @@ class GitLab extends AppSource { | ||||
|     } | ||||
|     var json = jsonDecode(res.body) as List<dynamic>; | ||||
|     Map<String, List<String>> results = {}; | ||||
|     json.forEach((element) { | ||||
|     for (var element in json) { | ||||
|       results['https://$host/${element['path_with_namespace']}'] = [ | ||||
|         element['name_with_namespace'], | ||||
|         element['description'] ?? tr('noDescription') | ||||
|       ]; | ||||
|     }); | ||||
|     } | ||||
|     return results; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -85,9 +85,12 @@ bool _isNumeric(String s) { | ||||
| } | ||||
|  | ||||
| class HTML extends AppSource { | ||||
|   HTML() { | ||||
|     overrideEligible = true; | ||||
|   } | ||||
|   @override | ||||
|   // TODO: implement requestHeaders choice, hardcoded for now | ||||
|   Map<String, String>? get requestHeaders => { | ||||
|         "User-Agent": | ||||
|             "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0" | ||||
|       }; | ||||
|  | ||||
|   @override | ||||
|   String sourceSpecificStandardizeURL(String url) { | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/app_sources/fdroid.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
| @@ -19,8 +18,8 @@ class IzzyOnDroid extends AppSource { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   String? tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) { | ||||
|   Future<String?> tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) async { | ||||
|     return FDroid().tryInferringAppId(standardUrl); | ||||
|   } | ||||
|  | ||||
| @@ -29,7 +28,7 @@ class IzzyOnDroid extends AppSource { | ||||
|     String standardUrl, | ||||
|     Map<String, dynamic> additionalSettings, | ||||
|   ) async { | ||||
|     String? appId = tryInferringAppId(standardUrl); | ||||
|     String? appId = await tryInferringAppId(standardUrl); | ||||
|     return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse( | ||||
|         await sourceRequest( | ||||
|             'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'), | ||||
|   | ||||
| @@ -6,11 +6,9 @@ import 'package:obtainium/providers/source_provider.dart'; | ||||
|  | ||||
| class Jenkins extends AppSource { | ||||
|   Jenkins() { | ||||
|     overrideEligible = true; | ||||
|     overrideVersionDetectionFormDefault('releaseDateAsVersion', true); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   String trimJobUrl(String url) { | ||||
|     RegExp standardUrlRegEx = RegExp('.*/job/[^/]+'); | ||||
|     RegExpMatch? match = standardUrlRegEx.firstMatch(url); | ||||
|   | ||||
| @@ -6,7 +6,6 @@ import 'package:obtainium/providers/source_provider.dart'; | ||||
| class SourceForge extends AppSource { | ||||
|   SourceForge() { | ||||
|     host = 'sourceforge.net'; | ||||
|     overrideEligible = true; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import 'package:html/parser.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/app_sources/github.dart'; | ||||
| import 'package:obtainium/app_sources/html.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
| @@ -10,7 +9,6 @@ import 'package:easy_localization/easy_localization.dart'; | ||||
| class SourceHut extends AppSource { | ||||
|   SourceHut() { | ||||
|     host = 'git.sr.ht'; | ||||
|     overrideEligible = true; | ||||
|  | ||||
|     additionalSourceAppSpecificSettingFormItems = [ | ||||
|       [ | ||||
| @@ -58,11 +56,19 @@ class SourceHut extends AppSource { | ||||
|           throw NoVersionError(); | ||||
|         } | ||||
|         String? releaseDateString = entry.querySelector('pubDate')?.innerHtml; | ||||
|         var link = entry.querySelector('link'); | ||||
|         String releasePage = '$standardUrl/refs/$version'; | ||||
|         DateTime? releaseDate = releaseDateString != null | ||||
|             ? DateFormat('EEE, dd MMM yyyy HH:mm:ss Z').parse(releaseDateString) | ||||
|             : null; | ||||
|         DateTime? releaseDate; | ||||
|         try { | ||||
|           releaseDate = releaseDateString != null | ||||
|               ? DateFormat('E, dd MMM yyyy HH:mm:ss Z').parse(releaseDateString) | ||||
|               : null; | ||||
|           releaseDate = releaseDateString != null | ||||
|               ? DateFormat('EEE, dd MMM yyyy HH:mm:ss Z') | ||||
|                   .parse(releaseDateString) | ||||
|               : null; | ||||
|         } catch (e) { | ||||
|           // ignore | ||||
|         } | ||||
|         var res2 = await sourceRequest(releasePage); | ||||
|         List<MapEntry<String, String>> apkUrls = []; | ||||
|         if (res2.statusCode == 200) { | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import 'package:html/parser.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/app_sources/html.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
|  | ||||
|   | ||||
| @@ -56,7 +56,7 @@ class InstallError extends ObtainiumError { | ||||
| } | ||||
|  | ||||
| class IDChangedError extends ObtainiumError { | ||||
|   IDChangedError() : super(tr('appIdMismatch')); | ||||
|   IDChangedError(String newId) : super('${tr('appIdMismatch')} - $newId'); | ||||
| } | ||||
|  | ||||
| class NotImplementedError extends ObtainiumError { | ||||
|   | ||||
| @@ -21,7 +21,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart'; | ||||
| // ignore: implementation_imports | ||||
| import 'package:easy_localization/src/localization.dart'; | ||||
|  | ||||
| const String currentVersion = '0.13.1'; | ||||
| const String currentVersion = '0.13.11'; | ||||
| const String currentReleaseTag = | ||||
|     'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES | ||||
|  | ||||
| @@ -37,6 +37,8 @@ List<MapEntry<Locale, String>> supportedLocales = const [ | ||||
|   MapEntry(Locale('fa'), 'فارسی'), | ||||
|   MapEntry(Locale('fr'), 'Français'), | ||||
|   MapEntry(Locale('es'), 'Español'), | ||||
|   MapEntry(Locale('pl'), 'Polski'), | ||||
|   MapEntry(Locale('ru'), 'Русский язык'), | ||||
| ]; | ||||
| const fallbackLocale = Locale('en'); | ||||
| const localeDir = 'assets/translations'; | ||||
|   | ||||
| @@ -33,6 +33,7 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|   AppSource? pickedSource; | ||||
|   Map<String, dynamic> additionalSettings = {}; | ||||
|   bool additionalSettingsValid = true; | ||||
|   bool inferAppIdIfOptional = true; | ||||
|   List<String> pickedCategories = []; | ||||
|   int searchnum = 0; | ||||
|   SourceProvider sourceProvider = SourceProvider(); | ||||
| @@ -78,6 +79,7 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|             additionalSettingsValid = source != null | ||||
|                 ? !sourceProvider.ifRequiredAppSpecificSettingsExist(source) | ||||
|                 : true; | ||||
|             inferAppIdIfOptional = true; | ||||
|           } | ||||
|         }); | ||||
|       } | ||||
| @@ -147,7 +149,8 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|           app = await sourceProvider.getApp( | ||||
|               pickedSource!, userInput, additionalSettings, | ||||
|               trackOnlyOverride: trackOnly, | ||||
|               overrideSource: pickedSourceOverride); | ||||
|               overrideSource: pickedSourceOverride, | ||||
|               inferAppIdIfOptional: inferAppIdIfOptional); | ||||
|           // Only download the APK here if you need to for the package ID | ||||
|           if (sourceProvider.isTempId(app) && | ||||
|               app.additionalSettings['trackOnly'] != true) { | ||||
| @@ -283,6 +286,9 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|           } | ||||
|           si++; | ||||
|         } | ||||
|         if (res.isEmpty) { | ||||
|           throw ObtainiumError(tr('noResults')); | ||||
|         } | ||||
|         List<String>? selectedUrls = res.isEmpty | ||||
|             ? [] | ||||
|             // ignore: use_build_context_synchronously | ||||
| @@ -318,10 +324,8 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|                         'overrideSource', | ||||
|                         defaultValue: HTML().runtimeType.toString(), | ||||
|                         [ | ||||
|                           ...sourceProvider.sources | ||||
|                               .where((s) => s.overrideEligible) | ||||
|                               .map((s) => | ||||
|                                   MapEntry(s.runtimeType.toString(), s.name)) | ||||
|                           ...sourceProvider.sources.map( | ||||
|                               (s) => MapEntry(s.runtimeType.toString(), s.name)) | ||||
|                         ], | ||||
|                         label: tr('overrideSource')) | ||||
|                   ] | ||||
| @@ -377,13 +381,15 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|             const SizedBox( | ||||
|               width: 16, | ||||
|             ), | ||||
|             ElevatedButton( | ||||
|                 onPressed: searchQuery.isEmpty || doingSomething | ||||
|                     ? null | ||||
|                     : () { | ||||
|                         runSearch(); | ||||
|                       }, | ||||
|                 child: Text(tr('search'))) | ||||
|             searching | ||||
|                 ? const CircularProgressIndicator() | ||||
|                 : ElevatedButton( | ||||
|                     onPressed: searchQuery.isEmpty || doingSomething | ||||
|                         ? null | ||||
|                         : () { | ||||
|                             runSearch(); | ||||
|                           }, | ||||
|                     child: Text(tr('search'))) | ||||
|           ], | ||||
|         ); | ||||
|  | ||||
| @@ -425,6 +431,23 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|                     }), | ||||
|               ], | ||||
|             ), | ||||
|             if (pickedSource != null && pickedSource!.appIdInferIsOptional) | ||||
|               GeneratedForm( | ||||
|                   key: const Key('inferAppIdIfOptional'), | ||||
|                   items: [ | ||||
|                     [ | ||||
|                       GeneratedFormSwitch('inferAppIdIfOptional', | ||||
|                           label: tr('tryInferAppIdFromCode'), | ||||
|                           defaultValue: inferAppIdIfOptional) | ||||
|                     ] | ||||
|                   ], | ||||
|                   onValueChanges: (values, valid, isBuilding) { | ||||
|                     if (!isBuilding) { | ||||
|                       setState(() { | ||||
|                         inferAppIdIfOptional = values['inferAppIdIfOptional']; | ||||
|                       }); | ||||
|                     } | ||||
|                   }), | ||||
|           ], | ||||
|         ); | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import 'dart:ui'; | ||||
|  | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| @@ -32,6 +34,7 @@ class _AppPageState extends State<AppPage> { | ||||
|     getUpdate(String id) { | ||||
|       appsProvider.checkUpdate(id).catchError((e) { | ||||
|         showError(e, context); | ||||
|         return null; | ||||
|       }); | ||||
|     } | ||||
|  | ||||
| @@ -81,20 +84,20 @@ class _AppPageState extends State<AppPage> { | ||||
|             const SizedBox( | ||||
|               height: 32, | ||||
|             ), | ||||
|             Text( | ||||
|               tr('latestVersionX', | ||||
|                   args: [app?.app.latestVersion ?? tr('unknown')]), | ||||
|               textAlign: TextAlign.center, | ||||
|               style: Theme.of(context).textTheme.bodyLarge, | ||||
|             ), | ||||
|             Text( | ||||
|               '${tr('installedVersionX', args: [ | ||||
|                     app?.app.installedVersion ?? tr('none') | ||||
|                   ])}${trackOnly ? ' ${tr('estimateInBrackets')}\n\n${tr('xIsTrackOnly', args: [ | ||||
|                       tr('app') | ||||
|                     ])}' : ''}', | ||||
|               textAlign: TextAlign.center, | ||||
|               style: Theme.of(context).textTheme.bodyLarge, | ||||
|             Column( | ||||
|               children: [ | ||||
|                 Text( | ||||
|                   '${tr('latestVersionX', args: [ | ||||
|                         app?.app.latestVersion ?? tr('unknown') | ||||
|                       ])}\n${tr('installedVersionX', args: [ | ||||
|                         app?.app.installedVersion ?? tr('none') | ||||
|                       ])}${trackOnly ? ' ${tr('estimateInBrackets')}\n\n${tr('xIsTrackOnly', args: [ | ||||
|                           tr('app') | ||||
|                         ])}' : ''}', | ||||
|                   textAlign: TextAlign.end, | ||||
|                   style: Theme.of(context).textTheme.bodyLarge!, | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|             if (app?.app.installedVersion != null && | ||||
|                 !isVersionDetectionStandard) | ||||
| @@ -328,7 +331,8 @@ class _AppPageState extends State<AppPage> { | ||||
|                 try { | ||||
|                   HapticFeedback.heavyImpact(); | ||||
|                   var res = await appsProvider.downloadAndInstallLatestApps( | ||||
|                       [app!.app.id], globalNavigatorKey.currentContext); | ||||
|                       app?.app.id != null ? [app!.app.id] : [], | ||||
|                       globalNavigatorKey.currentContext); | ||||
|                   if (res.isNotEmpty && mounted) { | ||||
|                     Navigator.of(context).pop(); | ||||
|                   } | ||||
| @@ -425,8 +429,10 @@ class _AppPageState extends State<AppPage> { | ||||
|                         onPressed: app?.downloadProgress != null | ||||
|                             ? null | ||||
|                             : () { | ||||
|                                 appsProvider.removeAppsWithModal( | ||||
|                                     context, [app!.app]).then((value) { | ||||
|                                 appsProvider | ||||
|                                     .removeAppsWithModal( | ||||
|                                         context, app != null ? [app.app] : []) | ||||
|                                     .then((value) { | ||||
|                                   if (value == true) { | ||||
|                                     Navigator.of(context).pop(); | ||||
|                                   } | ||||
| @@ -444,7 +450,9 @@ class _AppPageState extends State<AppPage> { | ||||
|               Padding( | ||||
|                   padding: const EdgeInsets.fromLTRB(0, 8, 0, 0), | ||||
|                   child: LinearProgressIndicator( | ||||
|                       value: app!.downloadProgress! / 100)) | ||||
|                       value: app!.downloadProgress! >= 0 | ||||
|                           ? app.downloadProgress! / 100 | ||||
|                           : null)) | ||||
|           ], | ||||
|         )); | ||||
|  | ||||
|   | ||||
| @@ -61,8 +61,6 @@ class AppsPageState extends State<AppsPage> { | ||||
|     var settingsProvider = context.watch<SettingsProvider>(); | ||||
|     var sourceProvider = SourceProvider(); | ||||
|     var listedApps = appsProvider.getAppValues().toList(); | ||||
|     var currentFilterIsUpdatesOnly = | ||||
|         filter.isIdenticalTo(updatesOnlyFilter, settingsProvider); | ||||
|  | ||||
|     refresh() { | ||||
|       HapticFeedback.lightImpact(); | ||||
| @@ -71,6 +69,7 @@ class AppsPageState extends State<AppsPage> { | ||||
|       }); | ||||
|       return appsProvider.checkUpdates().catchError((e) { | ||||
|         showError(e, context); | ||||
|         return <App>[]; | ||||
|       }).whenComplete(() { | ||||
|         setState(() { | ||||
|           refreshingSince = null; | ||||
| @@ -128,6 +127,11 @@ class AppsPageState extends State<AppsPage> { | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       if (filter.idFilter.isNotEmpty) { | ||||
|         if (!app.app.id.contains(filter.idFilter)) { | ||||
|           return false; | ||||
|         } | ||||
|       } | ||||
|       if (filter.categoryFilter.isNotEmpty && | ||||
|           filter.categoryFilter | ||||
|               .intersection(app.app.categories.toSet()) | ||||
| @@ -318,28 +322,28 @@ class AppsPageState extends State<AppsPage> { | ||||
|  | ||||
|     getLoadingWidgets() { | ||||
|       return [ | ||||
|         if (appsProvider.loadingApps || listedApps.isEmpty) | ||||
|         if (listedApps.isEmpty) | ||||
|           SliverFillRemaining( | ||||
|               child: Center( | ||||
|                   child: appsProvider.loadingApps | ||||
|                       ? const CircularProgressIndicator() | ||||
|                       : Text( | ||||
|                           appsProvider.apps.isEmpty | ||||
|                               ? tr('noApps') | ||||
|                               : tr('noAppsForFilter'), | ||||
|                           style: Theme.of(context).textTheme.headlineMedium, | ||||
|                           textAlign: TextAlign.center, | ||||
|                         ))), | ||||
|         if (refreshingSince != null) | ||||
|                   child: Text( | ||||
|             appsProvider.apps.isEmpty ? tr('noApps') : tr('noAppsForFilter'), | ||||
|             style: Theme.of(context).textTheme.headlineMedium, | ||||
|             textAlign: TextAlign.center, | ||||
|           ))), | ||||
|         if (refreshingSince != null || appsProvider.loadingApps) | ||||
|           SliverToBoxAdapter( | ||||
|             child: LinearProgressIndicator( | ||||
|               value: appsProvider | ||||
|                       .getAppValues() | ||||
|                       .where((element) => !(element.app.lastUpdateCheck | ||||
|                               ?.isBefore(refreshingSince!) ?? | ||||
|                           true)) | ||||
|                       .length / | ||||
|                   (appsProvider.apps.isNotEmpty ? appsProvider.apps.length : 1), | ||||
|               value: appsProvider.loadingApps | ||||
|                   ? null | ||||
|                   : appsProvider | ||||
|                           .getAppValues() | ||||
|                           .where((element) => !(element.app.lastUpdateCheck | ||||
|                                   ?.isBefore(refreshingSince!) ?? | ||||
|                               true)) | ||||
|                           .length / | ||||
|                       (appsProvider.apps.isNotEmpty | ||||
|                           ? appsProvider.apps.length | ||||
|                           : 1), | ||||
|             ), | ||||
|           ) | ||||
|       ]; | ||||
| @@ -379,6 +383,7 @@ class AppsPageState extends State<AppsPage> { | ||||
|                       [listedApps[appIndex].app.id], | ||||
|                       globalNavigatorKey.currentContext).catchError((e) { | ||||
|                     showError(e, context); | ||||
|                     return <String>[]; | ||||
|                   }); | ||||
|                 }, | ||||
|           icon: Icon( | ||||
| @@ -441,37 +446,35 @@ class AppsPageState extends State<AppsPage> { | ||||
|                   width: 10, | ||||
|                 ) | ||||
|               : const SizedBox.shrink(), | ||||
|           Column( | ||||
|             mainAxisAlignment: MainAxisAlignment.center, | ||||
|             crossAxisAlignment: CrossAxisAlignment.end, | ||||
|             children: [ | ||||
|               Row(mainAxisSize: MainAxisSize.min, children: [ | ||||
|                 Container( | ||||
|                     constraints: BoxConstraints( | ||||
|                         maxWidth: MediaQuery.of(context).size.width / 4), | ||||
|                     child: Text( | ||||
|                       getVersionText(index), | ||||
|                       overflow: TextOverflow.ellipsis, | ||||
|                       textAlign: TextAlign.end, | ||||
|                     )), | ||||
|               ]), | ||||
|               Row( | ||||
|                 mainAxisSize: MainAxisSize.min, | ||||
|           GestureDetector( | ||||
|               onTap: showChangesFn, | ||||
|               child: Column( | ||||
|                 mainAxisAlignment: MainAxisAlignment.center, | ||||
|                 crossAxisAlignment: CrossAxisAlignment.end, | ||||
|                 children: [ | ||||
|                   GestureDetector( | ||||
|                       onTap: showChangesFn, | ||||
|                       child: Text( | ||||
|                   Row(mainAxisSize: MainAxisSize.min, children: [ | ||||
|                     Container( | ||||
|                         constraints: BoxConstraints( | ||||
|                             maxWidth: MediaQuery.of(context).size.width / 4), | ||||
|                         child: Text(getVersionText(index), | ||||
|                             overflow: TextOverflow.ellipsis, | ||||
|                             textAlign: TextAlign.end)), | ||||
|                   ]), | ||||
|                   Row( | ||||
|                     mainAxisSize: MainAxisSize.min, | ||||
|                     children: [ | ||||
|                       Text( | ||||
|                         getChangesButtonString(index, showChangesFn != null), | ||||
|                         style: TextStyle( | ||||
|                             fontStyle: FontStyle.italic, | ||||
|                             decoration: showChangesFn != null | ||||
|                                 ? TextDecoration.underline | ||||
|                                 : TextDecoration.none), | ||||
|                       )) | ||||
|                       ) | ||||
|                     ], | ||||
|                   ), | ||||
|                 ], | ||||
|               ), | ||||
|             ], | ||||
|           ) | ||||
|               )) | ||||
|         ], | ||||
|       ); | ||||
|  | ||||
| @@ -540,11 +543,20 @@ class AppsPageState extends State<AppsPage> { | ||||
|                         : FontWeight.normal)), | ||||
|             trailing: listedApps[index].downloadProgress != null | ||||
|                 ? SizedBox( | ||||
|                     width: 110, | ||||
|                     child: Text(tr('percentProgress', args: [ | ||||
|                       listedApps[index].downloadProgress?.toInt().toString() ?? | ||||
|                           '100' | ||||
|                     ]))) | ||||
|                     width: 90, | ||||
|                     child: Text( | ||||
|                       listedApps[index].downloadProgress! >= 0 | ||||
|                           ? tr('percentProgress', args: [ | ||||
|                               listedApps[index] | ||||
|                                   .downloadProgress! | ||||
|                                   .toInt() | ||||
|                                   .toString() | ||||
|                             ]) | ||||
|                           : tr('pleaseWait'), | ||||
|                       textAlign: (listedApps[index].downloadProgress! >= 0) | ||||
|                           ? TextAlign.start | ||||
|                           : TextAlign.end, | ||||
|                     )) | ||||
|                 : trailingRow, | ||||
|             onTap: () { | ||||
|               if (selectedAppIds.isNotEmpty) { | ||||
| @@ -677,6 +689,7 @@ class AppsPageState extends State<AppsPage> { | ||||
|                           settingsProvider: settingsProvider) | ||||
|                       .catchError((e) { | ||||
|                     showError(e, context); | ||||
|                     return <String>[]; | ||||
|                   }); | ||||
|                 } | ||||
|               }); | ||||
| @@ -872,44 +885,41 @@ class AppsPageState extends State<AppsPage> { | ||||
|           }); | ||||
|     } | ||||
|  | ||||
|     getMainBottomButtonsRow() { | ||||
|       return Row( | ||||
|         mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||
|         children: [ | ||||
|           IconButton( | ||||
|     getMainBottomButtons() { | ||||
|       return [ | ||||
|         IconButton( | ||||
|             visualDensity: VisualDensity.compact, | ||||
|             onPressed: selectedAppIds.isEmpty | ||||
|                 ? null | ||||
|                 : () { | ||||
|                     appsProvider.removeAppsWithModal( | ||||
|                         context, selectedApps.toList()); | ||||
|                   }, | ||||
|             tooltip: tr('removeSelectedApps'), | ||||
|             icon: const Icon(Icons.delete_outline_outlined), | ||||
|           ), | ||||
|           IconButton( | ||||
|               visualDensity: VisualDensity.compact, | ||||
|               onPressed: getMassObtainFunction(), | ||||
|               tooltip: selectedAppIds.isEmpty | ||||
|                   ? tr('installUpdateApps') | ||||
|                   : tr('installUpdateSelectedApps'), | ||||
|               icon: const Icon( | ||||
|                 Icons.file_download_outlined, | ||||
|               )), | ||||
|           IconButton( | ||||
|             visualDensity: VisualDensity.compact, | ||||
|             onPressed: selectedAppIds.isEmpty ? null : launchCategorizeDialog(), | ||||
|             tooltip: tr('categorize'), | ||||
|             icon: const Icon(Icons.category_outlined), | ||||
|           ), | ||||
|           IconButton( | ||||
|             visualDensity: VisualDensity.compact, | ||||
|             onPressed: selectedAppIds.isEmpty ? null : showMoreOptionsDialog, | ||||
|             tooltip: tr('more'), | ||||
|             icon: const Icon(Icons.more_horiz), | ||||
|           ), | ||||
|         ], | ||||
|       ); | ||||
|             onPressed: getMassObtainFunction(), | ||||
|             tooltip: selectedAppIds.isEmpty | ||||
|                 ? tr('installUpdateApps') | ||||
|                 : tr('installUpdateSelectedApps'), | ||||
|             icon: const Icon( | ||||
|               Icons.file_download_outlined, | ||||
|             )), | ||||
|         IconButton( | ||||
|           visualDensity: VisualDensity.compact, | ||||
|           onPressed: selectedAppIds.isEmpty | ||||
|               ? null | ||||
|               : () { | ||||
|                   appsProvider.removeAppsWithModal( | ||||
|                       context, selectedApps.toList()); | ||||
|                 }, | ||||
|           tooltip: tr('removeSelectedApps'), | ||||
|           icon: const Icon(Icons.delete_outline_outlined), | ||||
|         ), | ||||
|         IconButton( | ||||
|           visualDensity: VisualDensity.compact, | ||||
|           onPressed: selectedAppIds.isEmpty ? null : launchCategorizeDialog(), | ||||
|           tooltip: tr('categorize'), | ||||
|           icon: const Icon(Icons.category_outlined), | ||||
|         ), | ||||
|         IconButton( | ||||
|           visualDensity: VisualDensity.compact, | ||||
|           onPressed: selectedAppIds.isEmpty ? null : showMoreOptionsDialog, | ||||
|           tooltip: tr('more'), | ||||
|           icon: const Icon(Icons.more_horiz), | ||||
|         ), | ||||
|       ]; | ||||
|     } | ||||
|  | ||||
|     showFilterDialog() async { | ||||
| @@ -931,6 +941,12 @@ class AppsPageState extends State<AppsPage> { | ||||
|                       required: false, | ||||
|                       defaultValue: vals['author']) | ||||
|                 ], | ||||
|                 [ | ||||
|                   GeneratedFormTextField('appId', | ||||
|                       label: tr('appId'), | ||||
|                       required: false, | ||||
|                       defaultValue: vals['appId']) | ||||
|                 ], | ||||
|                 [ | ||||
|                   GeneratedFormSwitch('upToDateApps', | ||||
|                       label: tr('upToDateApps'), | ||||
| @@ -976,50 +992,33 @@ class AppsPageState extends State<AppsPage> { | ||||
|     } | ||||
|  | ||||
|     getFilterButtonsRow() { | ||||
|       var isFilterOff = filter.isIdenticalTo(neutralFilter, settingsProvider); | ||||
|       return Row( | ||||
|         children: [ | ||||
|           getSelectAllButton(), | ||||
|           IconButton( | ||||
|               color: Theme.of(context).colorScheme.primary, | ||||
|               style: const ButtonStyle(visualDensity: VisualDensity.compact), | ||||
|               tooltip: isFilterOff ? tr('filter') : tr('filterActive'), | ||||
|               onPressed: isFilterOff | ||||
|                   ? showFilterDialog | ||||
|                   : () { | ||||
|                       setState(() { | ||||
|                         filter = AppsFilter(); | ||||
|                       }); | ||||
|                     }, | ||||
|               icon: Icon(isFilterOff | ||||
|                   ? Icons.filter_list_rounded | ||||
|                   : Icons.filter_list_off_rounded)), | ||||
|           const SizedBox( | ||||
|             width: 10, | ||||
|           ), | ||||
|           const VerticalDivider(), | ||||
|           Expanded( | ||||
|               child: SingleChildScrollView( | ||||
|                   scrollDirection: Axis.horizontal, | ||||
|                   child: getMainBottomButtonsRow())), | ||||
|           const VerticalDivider(), | ||||
|           IconButton( | ||||
|             visualDensity: VisualDensity.compact, | ||||
|             onPressed: () { | ||||
|               setState(() { | ||||
|                 if (currentFilterIsUpdatesOnly) { | ||||
|                   filter = AppsFilter(); | ||||
|                 } else { | ||||
|                   filter = updatesOnlyFilter; | ||||
|                 } | ||||
|               }); | ||||
|             }, | ||||
|             tooltip: currentFilterIsUpdatesOnly | ||||
|                 ? tr('removeOutdatedFilter') | ||||
|                 : tr('showOutdatedOnly'), | ||||
|             icon: Icon( | ||||
|               currentFilterIsUpdatesOnly | ||||
|                   ? Icons.update_disabled_rounded | ||||
|                   : Icons.update_rounded, | ||||
|               color: Theme.of(context).colorScheme.primary, | ||||
|             ), | ||||
|           ), | ||||
|           TextButton.icon( | ||||
|               style: const ButtonStyle(visualDensity: VisualDensity.compact), | ||||
|               label: Text( | ||||
|                 filter.isIdenticalTo(neutralFilter, settingsProvider) | ||||
|                     ? tr('filter') | ||||
|                     : tr('filterActive'), | ||||
|                 style: TextStyle( | ||||
|                     fontWeight: | ||||
|                         filter.isIdenticalTo(neutralFilter, settingsProvider) | ||||
|                             ? FontWeight.normal | ||||
|                             : FontWeight.bold), | ||||
|               ), | ||||
|               onPressed: showFilterDialog, | ||||
|               icon: const Icon(Icons.filter_list_rounded)) | ||||
|               child: Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||
|             children: getMainBottomButtons(), | ||||
|           )), | ||||
|         ], | ||||
|       ); | ||||
|     } | ||||
| @@ -1062,6 +1061,7 @@ class AppsPageState extends State<AppsPage> { | ||||
| class AppsFilter { | ||||
|   late String nameFilter; | ||||
|   late String authorFilter; | ||||
|   late String idFilter; | ||||
|   late bool includeUptodate; | ||||
|   late bool includeNonInstalled; | ||||
|   late Set<String> categoryFilter; | ||||
| @@ -1070,6 +1070,7 @@ class AppsFilter { | ||||
|   AppsFilter( | ||||
|       {this.nameFilter = '', | ||||
|       this.authorFilter = '', | ||||
|       this.idFilter = '', | ||||
|       this.includeUptodate = true, | ||||
|       this.includeNonInstalled = true, | ||||
|       this.categoryFilter = const {}, | ||||
| @@ -1079,6 +1080,7 @@ class AppsFilter { | ||||
|     return { | ||||
|       'appName': nameFilter, | ||||
|       'author': authorFilter, | ||||
|       'appId': idFilter, | ||||
|       'upToDateApps': includeUptodate, | ||||
|       'nonInstalledApps': includeNonInstalled, | ||||
|       'sourceFilter': sourceFilter | ||||
| @@ -1088,6 +1090,7 @@ class AppsFilter { | ||||
|   setFormValuesFromMap(Map<String, dynamic> values) { | ||||
|     nameFilter = values['appName']!; | ||||
|     authorFilter = values['author']!; | ||||
|     idFilter = values['appId']!; | ||||
|     includeUptodate = values['upToDateApps']; | ||||
|     includeNonInstalled = values['nonInstalledApps']; | ||||
|     sourceFilter = values['sourceFilter']; | ||||
| @@ -1096,6 +1099,7 @@ class AppsFilter { | ||||
|   bool isIdenticalTo(AppsFilter other, SettingsProvider settingsProvider) => | ||||
|       authorFilter.trim() == other.authorFilter.trim() && | ||||
|       nameFilter.trim() == other.nameFilter.trim() && | ||||
|       idFilter.trim() == other.idFilter.trim() && | ||||
|       includeUptodate == other.includeUptodate && | ||||
|       includeNonInstalled == other.includeNonInstalled && | ||||
|       settingsProvider.setEqual(categoryFilter, other.categoryFilter) && | ||||
|   | ||||
| @@ -27,6 +27,7 @@ class NavigationPageItem { | ||||
| class _HomePageState extends State<HomePage> { | ||||
|   List<int> selectedIndexHistory = []; | ||||
|   int prevAppCount = -1; | ||||
|   bool prevIsLoading = true; | ||||
|  | ||||
|   List<NavigationPageItem> pages = [ | ||||
|     NavigationPageItem(tr('appsString'), Icons.apps, | ||||
| @@ -64,13 +65,15 @@ class _HomePageState extends State<HomePage> { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (prevAppCount >= 0 && | ||||
|     if (!prevIsLoading && | ||||
|         prevAppCount >= 0 && | ||||
|         appsProvider.apps.length > prevAppCount && | ||||
|         selectedIndexHistory.isNotEmpty && | ||||
|         selectedIndexHistory.last == 1) { | ||||
|       switchToPage(0); | ||||
|     } | ||||
|     prevAppCount = appsProvider.apps.length; | ||||
|     prevIsLoading = appsProvider.loadingApps; | ||||
|  | ||||
|     return WillPopScope( | ||||
|         child: Scaffold( | ||||
|   | ||||
| @@ -323,8 +323,8 @@ class _ImportExportPageState extends State<ImportExportPage> { | ||||
|                         ], | ||||
|                       ), | ||||
|                       if (importInProgress) | ||||
|                         Column( | ||||
|                           children: const [ | ||||
|                         const Column( | ||||
|                           children: [ | ||||
|                             SizedBox( | ||||
|                               height: 14, | ||||
|                             ), | ||||
|   | ||||
| @@ -27,7 +27,7 @@ import 'package:flutter_fgbg/flutter_fgbg.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:android_intent_plus/android_intent.dart'; | ||||
| import 'package:archive/archive.dart'; | ||||
| import 'package:archive/archive_io.dart'; | ||||
|  | ||||
| class AppInMemory { | ||||
|   late App app; | ||||
| @@ -108,6 +108,7 @@ class AppsProvider with ChangeNotifier { | ||||
|   bool isForeground = true; | ||||
|   late Stream<FGBGType>? foregroundStream; | ||||
|   late StreamSubscription<FGBGType>? foregroundSubscription; | ||||
|   late Directory APKDir; | ||||
|  | ||||
|   Iterable<AppInMemory> getAppValues() => apps.values.map((a) => a.deepCopy()); | ||||
|  | ||||
| @@ -116,35 +117,56 @@ class AppsProvider with ChangeNotifier { | ||||
|     foregroundStream = FGBGEvents.stream.asBroadcastStream(); | ||||
|     foregroundSubscription = foregroundStream?.listen((event) async { | ||||
|       isForeground = event == FGBGType.foreground; | ||||
|       if (isForeground) await loadApps(); | ||||
|       if (isForeground) await refreshInstallStatuses(); | ||||
|     }); | ||||
|     () async { | ||||
|       var cacheDirs = await getExternalCacheDirectories(); | ||||
|       if (cacheDirs?.isNotEmpty ?? false) { | ||||
|         APKDir = cacheDirs!.first; | ||||
|       } else { | ||||
|         APKDir = | ||||
|             Directory('${(await getExternalStorageDirectory())!.path}/apks'); | ||||
|         if (!APKDir.existsSync()) { | ||||
|           APKDir.createSync(); | ||||
|         } | ||||
|       } | ||||
|       // Load Apps into memory (in background, this is done later instead of in the constructor) | ||||
|       await loadApps(); | ||||
|       // Delete any partial APKs | ||||
|       (await getExternalCacheDirectories()) | ||||
|           ?.first | ||||
|           .listSync() | ||||
|           .where((element) => element.path.endsWith('.apk.part')) | ||||
|       var cutoff = DateTime.now().subtract(const Duration(days: 7)); | ||||
|       APKDir.listSync() | ||||
|           .where((element) => | ||||
|               element.path.endsWith('.part') || | ||||
|               element.statSync().modified.isBefore(cutoff)) | ||||
|           .forEach((partialApk) { | ||||
|         partialApk.delete(); | ||||
|         partialApk.delete(recursive: true); | ||||
|       }); | ||||
|     }(); | ||||
|   } | ||||
|  | ||||
|   downloadFile(String url, String fileName, Function? onProgress, | ||||
|   Future<File> downloadFile( | ||||
|       String url, String fileNameNoExt, Function? onProgress, | ||||
|       {bool useExisting = true, Map<String, String>? headers}) async { | ||||
|     var destDir = (await getExternalCacheDirectories())!.first.path; | ||||
|     var destDir = APKDir.path; | ||||
|     var req = Request('GET', Uri.parse(url)); | ||||
|     if (headers != null) { | ||||
|       req.headers.addAll(headers); | ||||
|     } | ||||
|     StreamedResponse response = await Client().send(req); | ||||
|     File downloadedFile = File('$destDir/$fileName'); | ||||
|     var client = Client(); | ||||
|     StreamedResponse response = await client.send(req); | ||||
|     String ext = | ||||
|         response.headers['content-disposition']?.split('.').last ?? 'apk'; | ||||
|     if (ext.endsWith('"') || ext.endsWith("other")) { | ||||
|       ext = ext.substring(0, ext.length - 1); | ||||
|     } | ||||
|     if (url.toLowerCase().endsWith('.apk') && ext != 'apk') { | ||||
|       ext = 'apk'; | ||||
|     } | ||||
|     File downloadedFile = File('$destDir/$fileNameNoExt.$ext'); | ||||
|     if (!(downloadedFile.existsSync() && useExisting)) { | ||||
|       File tempDownloadedFile = File('${downloadedFile.path}.part'); | ||||
|       if (tempDownloadedFile.existsSync()) { | ||||
|         tempDownloadedFile.deleteSync(); | ||||
|         tempDownloadedFile.deleteSync(recursive: true); | ||||
|       } | ||||
|       var length = response.contentLength; | ||||
|       var received = 0; | ||||
| @@ -164,32 +186,37 @@ class AppsProvider with ChangeNotifier { | ||||
|         onProgress(progress); | ||||
|       } | ||||
|       if (response.statusCode != 200) { | ||||
|         tempDownloadedFile.deleteSync(); | ||||
|         tempDownloadedFile.deleteSync(recursive: true); | ||||
|         throw response.reasonPhrase ?? tr('unexpectedError'); | ||||
|       } | ||||
|       tempDownloadedFile.renameSync(downloadedFile.path); | ||||
|     } else { | ||||
|       client.close(); | ||||
|     } | ||||
|     return downloadedFile; | ||||
|   } | ||||
|  | ||||
|   handleAPKIDChange(App app, PackageArchiveInfo newInfo, File downloadedFile, | ||||
|       String downloadUrl) async { | ||||
|   Future<File> handleAPKIDChange(App app, PackageArchiveInfo newInfo, | ||||
|       File downloadedFile, String downloadUrl) async { | ||||
|     // If the APK package ID is different from the App ID, it is either new (using a placeholder ID) or the ID has changed | ||||
|     // The former case should be handled (give the App its real ID), the latter is a security issue | ||||
|     if (app.id != newInfo.packageName) { | ||||
|       var isTempId = SourceProvider().isTempId(app); | ||||
|       if (apps[app.id] != null && !isTempId) { | ||||
|         throw IDChangedError(); | ||||
|       if (apps[app.id] != null && !isTempId && !app.allowIdChange) { | ||||
|         throw IDChangedError(newInfo.packageName); | ||||
|       } | ||||
|       var idChangeWasAllowed = app.allowIdChange; | ||||
|       app.allowIdChange = false; | ||||
|       var originalAppId = app.id; | ||||
|       app.id = newInfo.packageName; | ||||
|       downloadedFile = downloadedFile.renameSync( | ||||
|           '${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.apk'); | ||||
|           '${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.${downloadedFile.path.split('.').last}'); | ||||
|       if (apps[originalAppId] != null) { | ||||
|         await removeApps([originalAppId]); | ||||
|         await saveApps([app], onlyIfExists: !isTempId); | ||||
|         await saveApps([app], onlyIfExists: !isTempId && !idChangeWasAllowed); | ||||
|       } | ||||
|     } | ||||
|     return downloadedFile; | ||||
|   } | ||||
|  | ||||
|   Future<Object> downloadApp(App app, BuildContext? context) async { | ||||
| @@ -205,11 +232,11 @@ class AppsProvider with ChangeNotifier { | ||||
|           .getSource(app.url, overrideSource: app.overrideSource); | ||||
|       String downloadUrl = await source.apkUrlPrefetchModifier( | ||||
|           app.apkUrls[app.preferredApkIndex].value, app.url); | ||||
|       var fileName = '${app.id}-${downloadUrl.hashCode}.apk'; | ||||
|       var notif = DownloadNotification(app.finalName, 100); | ||||
|       notificationsProvider?.cancel(notif.id); | ||||
|       int? prevProg; | ||||
|       File downloadedFile = await downloadFile(downloadUrl, fileName, | ||||
|       var fileNameNoExt = '${app.id}-${downloadUrl.hashCode}'; | ||||
|       var downloadedFile = await downloadFile(downloadUrl, fileNameNoExt, | ||||
|           headers: source.requestHeaders, (double? progress) { | ||||
|         int? prog = progress?.ceil(); | ||||
|         if (apps[app.id] != null) { | ||||
| @@ -222,18 +249,20 @@ class AppsProvider with ChangeNotifier { | ||||
|         } | ||||
|         prevProg = prog; | ||||
|       }); | ||||
|       PackageArchiveInfo? newInfo; | ||||
|       try { | ||||
|         newInfo = await PackageArchiveInfo.fromPath(downloadedFile.path); | ||||
|       } catch (e) { | ||||
|         // Assume it's an XAPK | ||||
|         fileName = '${app.id}-${downloadUrl.hashCode}.xapk'; | ||||
|         String newPath = '${downloadedFile.parent.path}/$fileName'; | ||||
|         downloadedFile.renameSync(newPath); | ||||
|         downloadedFile = File(newPath); | ||||
|       // Set to 90 for remaining steps, will make null in 'finally' | ||||
|       if (apps[app.id] != null) { | ||||
|         apps[app.id]!.downloadProgress = -1; | ||||
|         notifyListeners(); | ||||
|         notif = DownloadNotification(app.finalName, -1); | ||||
|         notificationsProvider?.notify(notif); | ||||
|       } | ||||
|       PackageArchiveInfo? newInfo; | ||||
|       var isAPK = downloadedFile.path.toLowerCase().endsWith('.apk'); | ||||
|       Directory? xapkDir; | ||||
|       if (newInfo == null) { | ||||
|       if (isAPK) { | ||||
|         newInfo = await PackageArchiveInfo.fromPath(downloadedFile.path); | ||||
|       } else { | ||||
|         // Assume XAPK | ||||
|         String xapkDirPath = '${downloadedFile.path}-dir'; | ||||
|         unzipFile(downloadedFile.path, '${downloadedFile.path}-dir'); | ||||
|         xapkDir = Directory(xapkDirPath); | ||||
| @@ -243,20 +272,21 @@ class AppsProvider with ChangeNotifier { | ||||
|             .toList(); | ||||
|         newInfo = await PackageArchiveInfo.fromPath(apks.first.path); | ||||
|       } | ||||
|       await handleAPKIDChange(app, newInfo, downloadedFile, downloadUrl); | ||||
|       downloadedFile = | ||||
|           await handleAPKIDChange(app, newInfo, downloadedFile, downloadUrl); | ||||
|       // Delete older versions of the file if any | ||||
|       for (var file in downloadedFile.parent.listSync()) { | ||||
|         var fn = file.path.split('/').last; | ||||
|         if (fn.startsWith('${app.id}-') && | ||||
|             fn.toLowerCase().endsWith(xapkDir == null ? '.apk' : '.xapk') && | ||||
|             fn != downloadedFile.path.split('/').last) { | ||||
|           file.delete(); | ||||
|             FileSystemEntity.isFileSync(file.path) && | ||||
|             file.path != downloadedFile.path) { | ||||
|           file.delete(recursive: true); | ||||
|         } | ||||
|       } | ||||
|       if (xapkDir != null) { | ||||
|         return DownloadedXApkDir(app.id, downloadedFile, xapkDir); | ||||
|       } else { | ||||
|       if (isAPK) { | ||||
|         return DownloadedApk(app.id, downloadedFile); | ||||
|       } else { | ||||
|         return DownloadedXApkDir(app.id, downloadedFile, xapkDir!); | ||||
|       } | ||||
|     } finally { | ||||
|       notificationsProvider?.cancel(notifId); | ||||
| @@ -305,37 +335,31 @@ class AppsProvider with ChangeNotifier { | ||||
|   } | ||||
|  | ||||
|   void unzipFile(String filePath, String destinationPath) { | ||||
|     final bytes = File(filePath).readAsBytesSync(); | ||||
|     final archive = ZipDecoder().decodeBytes(bytes); | ||||
|  | ||||
|     for (final file in archive) { | ||||
|       final filename = '$destinationPath/${file.name}'; | ||||
|       if (file.isFile) { | ||||
|         final data = file.content as List<int>; | ||||
|         File(filename) | ||||
|           ..createSync(recursive: true) | ||||
|           ..writeAsBytesSync(data); | ||||
|       } else { | ||||
|         Directory(filename).create(recursive: true); | ||||
|       } | ||||
|     } | ||||
|     final inputStream = InputFileStream(filePath); | ||||
|     final archive = ZipDecoder().decodeBuffer(inputStream); | ||||
|     extractArchiveToDisk(archive, destinationPath); | ||||
|   } | ||||
|  | ||||
|   Future<void> installXApkDir(DownloadedXApkDir dir, | ||||
|       {bool silent = false}) async { | ||||
|     try { | ||||
|       var somethingInstalled = false; | ||||
|       for (var apk in dir.extracted | ||||
|           .listSync() | ||||
|           .where((f) => f is File && f.path.toLowerCase().endsWith('.apk'))) { | ||||
|         await installApk(DownloadedApk(dir.appId, apk as File), silent: silent); | ||||
|         somethingInstalled = somethingInstalled || | ||||
|             await installApk(DownloadedApk(dir.appId, apk as File), | ||||
|                 silent: silent); | ||||
|       } | ||||
|       if (somethingInstalled) { | ||||
|         dir.file.delete(recursive: true); | ||||
|       } | ||||
|       dir.file.delete(); | ||||
|     } finally { | ||||
|       dir.extracted.delete(recursive: true); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<void> installApk(DownloadedApk file, {bool silent = false}) async { | ||||
|   Future<bool> installApk(DownloadedApk file, {bool silent = false}) async { | ||||
|     // TODO: Use 'silent' when/if ever possible | ||||
|     var newInfo = await PackageArchiveInfo.fromPath(file.file.path); | ||||
|     AppInfo? appInfo; | ||||
| @@ -351,14 +375,17 @@ class AppsProvider with ChangeNotifier { | ||||
|     } | ||||
|     int? code = | ||||
|         await AndroidPackageInstaller.installApk(apkFilePath: file.file.path); | ||||
|     bool installed = false; | ||||
|     if (code != null && code != 0 && code != 3) { | ||||
|       throw InstallError(code); | ||||
|     } else if (code == 0) { | ||||
|       installed = true; | ||||
|       apps[file.appId]!.app.installedVersion = | ||||
|           apps[file.appId]!.app.latestVersion; | ||||
|       file.file.delete(); | ||||
|       file.file.delete(recursive: true); | ||||
|     } | ||||
|     await saveApps([apps[file.appId]!.app]); | ||||
|     return installed; | ||||
|   } | ||||
|  | ||||
|   void uninstallApp(String appId) async { | ||||
| @@ -503,10 +530,17 @@ class AppsProvider with ChangeNotifier { | ||||
|           // ignore: use_build_context_synchronously | ||||
|           await waitForUserToReturnToForeground(context); | ||||
|         } | ||||
|         if (downloadedFile != null) { | ||||
|           await installApk(downloadedFile, silent: willBeSilent); | ||||
|         } else { | ||||
|           await installXApkDir(downloadedDir!, silent: willBeSilent); | ||||
|         apps[id]?.downloadProgress = -1; | ||||
|         notifyListeners(); | ||||
|         try { | ||||
|           if (downloadedFile != null) { | ||||
|             await installApk(downloadedFile, silent: willBeSilent); | ||||
|           } else { | ||||
|             await installXApkDir(downloadedDir!, silent: willBeSilent); | ||||
|           } | ||||
|         } finally { | ||||
|           apps[id]?.downloadProgress = null; | ||||
|           notifyListeners(); | ||||
|         } | ||||
|         installedIds.add(id); | ||||
|       } catch (e) { | ||||
| @@ -672,41 +706,30 @@ class AppsProvider with ChangeNotifier { | ||||
|     } | ||||
|     loadingApps = true; | ||||
|     notifyListeners(); | ||||
|     List<App> newApps = (await getAppsDir()) | ||||
|         .listSync() | ||||
|         .where((item) => item.path.toLowerCase().endsWith('.json')) | ||||
|         .map((e) { | ||||
|           try { | ||||
|             return App.fromJson(jsonDecode(File(e.path).readAsStringSync())); | ||||
|           } catch (err) { | ||||
|             if (err is FormatException) { | ||||
|               logs.add('Corrupt JSON when loading App (will be ignored): $e'); | ||||
|               e.renameSync('${e.path}.corrupt'); | ||||
|               return App( | ||||
|                   '', '', '', '', '', '', [], 0, {}, DateTime.now(), false); | ||||
|             } else { | ||||
|               rethrow; | ||||
|             } | ||||
|           } | ||||
|         }) | ||||
|         .where((element) => element.id.isNotEmpty) | ||||
|         .toList(); | ||||
|     var idsToDelete = apps.values | ||||
|         .map((e) => e.app.id) | ||||
|         .toSet() | ||||
|         .difference(newApps.map((e) => e.id).toSet()); | ||||
|     for (var id in idsToDelete) { | ||||
|       apps.remove(id); | ||||
|     } | ||||
|     var sp = SourceProvider(); | ||||
|     List<List<String>> errors = []; | ||||
|     for (int i = 0; i < newApps.length; i++) { | ||||
|       var info = await getInstalledInfo(newApps[i].id); | ||||
|     List<FileSystemEntity> newApps = (await getAppsDir()) | ||||
|         .listSync() | ||||
|         .where((item) => item.path.toLowerCase().endsWith('.json')) | ||||
|         .toList(); | ||||
|     for (var e in newApps) { | ||||
|       try { | ||||
|         sp.getSource(newApps[i].url, overrideSource: newApps[i].overrideSource); | ||||
|         apps[newApps[i].id] = AppInMemory(newApps[i], null, info); | ||||
|       } catch (e) { | ||||
|         errors.add([newApps[i].id, newApps[i].finalName, e.toString()]); | ||||
|         var app = App.fromJson(jsonDecode(File(e.path).readAsStringSync())); | ||||
|         try { | ||||
|           var info = await getInstalledInfo(app.id); | ||||
|           sp.getSource(app.url, overrideSource: app.overrideSource); | ||||
|           apps[app.id] = AppInMemory(app, null, info); | ||||
|           notifyListeners(); | ||||
|         } catch (e) { | ||||
|           errors.add([app.id, app.finalName, e.toString()]); | ||||
|         } | ||||
|       } catch (err) { | ||||
|         if (err is FormatException) { | ||||
|           logs.add('Corrupt JSON when loading App (will be ignored): $e'); | ||||
|           e.renameSync('${e.path}.corrupt'); | ||||
|         } else { | ||||
|           rethrow; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     if (errors.isNotEmpty) { | ||||
| @@ -716,6 +739,10 @@ class AppsProvider with ChangeNotifier { | ||||
|     } | ||||
|     loadingApps = false; | ||||
|     notifyListeners(); | ||||
|     refreshInstallStatuses(); | ||||
|   } | ||||
|  | ||||
|   Future<void> refreshInstallStatuses() async { | ||||
|     if (await doesInstalledAppsPluginWork()) { | ||||
|       List<App> modifiedApps = []; | ||||
|       for (var app in apps.values) { | ||||
| @@ -759,11 +786,18 @@ class AppsProvider with ChangeNotifier { | ||||
|   } | ||||
|  | ||||
|   Future<void> removeApps(List<String> appIds) async { | ||||
|     var apkFiles = APKDir.listSync(); | ||||
|     for (var appId in appIds) { | ||||
|       File file = File('${(await getAppsDir()).path}/$appId.json'); | ||||
|       if (file.existsSync()) { | ||||
|         file.deleteSync(); | ||||
|         file.deleteSync(recursive: true); | ||||
|       } | ||||
|       apkFiles | ||||
|           .where( | ||||
|               (element) => element.path.split('/').last.startsWith('$appId-')) | ||||
|           .forEach((element) { | ||||
|         element.delete(recursive: true); | ||||
|       }); | ||||
|       if (apps.containsKey(appId)) { | ||||
|         apps.remove(appId); | ||||
|       } | ||||
|   | ||||
| @@ -167,7 +167,8 @@ class NotificationsProvider { | ||||
|                 progress: progPercent ?? 0, | ||||
|                 maxProgress: 100, | ||||
|                 showProgress: progPercent != null, | ||||
|                 onlyAlertOnce: onlyAlertOnce))); | ||||
|                 onlyAlertOnce: onlyAlertOnce, | ||||
|                 indeterminate: progPercent != null && progPercent < 0))); | ||||
|   } | ||||
|  | ||||
|   Future<void> notify(ObtainiumNotification notif, | ||||
|   | ||||
| @@ -7,7 +7,6 @@ import 'package:device_info_plus/device_info_plus.dart'; | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:html/dom.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/app_sources/apkcombo.dart'; | ||||
| import 'package:obtainium/app_sources/apkmirror.dart'; | ||||
| import 'package:obtainium/app_sources/apkpure.dart'; | ||||
| import 'package:obtainium/app_sources/codeberg.dart'; | ||||
| @@ -164,6 +163,7 @@ class App { | ||||
|   late DateTime? releaseDate; | ||||
|   late String? changeLog; | ||||
|   late String? overrideSource; | ||||
|   bool allowIdChange = false; | ||||
|   App( | ||||
|       this.id, | ||||
|       this.url, | ||||
| @@ -179,7 +179,8 @@ class App { | ||||
|       {this.categories = const [], | ||||
|       this.releaseDate, | ||||
|       this.changeLog, | ||||
|       this.overrideSource}); | ||||
|       this.overrideSource, | ||||
|       this.allowIdChange = false}); | ||||
|  | ||||
|   @override | ||||
|   String toString() { | ||||
| @@ -210,7 +211,8 @@ class App { | ||||
|       categories: categories, | ||||
|       changeLog: changeLog, | ||||
|       releaseDate: releaseDate, | ||||
|       overrideSource: overrideSource); | ||||
|       overrideSource: overrideSource, | ||||
|       allowIdChange: allowIdChange); | ||||
|  | ||||
|   factory App.fromJson(Map<String, dynamic> json) { | ||||
|     json = appJSONCompatibilityModifiers(json); | ||||
| @@ -242,7 +244,8 @@ class App { | ||||
|             : DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']), | ||||
|         changeLog: | ||||
|             json['changeLog'] == null ? null : json['changeLog'] as String, | ||||
|         overrideSource: json['overrideSource']); | ||||
|         overrideSource: json['overrideSource'], | ||||
|         allowIdChange: json['allowIdChange'] ?? false); | ||||
|   } | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
| @@ -260,7 +263,8 @@ class App { | ||||
|         'categories': categories, | ||||
|         'releaseDate': releaseDate?.microsecondsSinceEpoch, | ||||
|         'changeLog': changeLog, | ||||
|         'overrideSource': overrideSource | ||||
|         'overrideSource': overrideSource, | ||||
|         'allowIdChange': allowIdChange | ||||
|       }; | ||||
| } | ||||
|  | ||||
| @@ -318,7 +322,7 @@ abstract class AppSource { | ||||
|   late String name; | ||||
|   bool enforceTrackOnly = false; | ||||
|   bool changeLogIfAnyIsMarkDown = true; | ||||
|   bool overrideEligible = false; | ||||
|   bool appIdInferIsOptional = false; | ||||
|  | ||||
|   AppSource() { | ||||
|     name = runtimeType.toString(); | ||||
| @@ -436,8 +440,8 @@ abstract class AppSource { | ||||
|     throw NotImplementedError(); | ||||
|   } | ||||
|  | ||||
|   String? tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) { | ||||
|   Future<String?> tryInferringAppId(String standardUrl, | ||||
|       {Map<String, dynamic> additionalSettings = const {}}) async { | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| @@ -554,7 +558,8 @@ class SourceProvider { | ||||
|       AppSource source, String url, Map<String, dynamic> additionalSettings, | ||||
|       {App? currentApp, | ||||
|       bool trackOnlyOverride = false, | ||||
|       String? overrideSource}) async { | ||||
|       String? overrideSource, | ||||
|       bool inferAppIdIfOptional = false}) async { | ||||
|     if (trackOnlyOverride || source.enforceTrackOnly) { | ||||
|       additionalSettings['trackOnly'] = true; | ||||
|     } | ||||
| @@ -594,8 +599,11 @@ class SourceProvider { | ||||
|         : apk.names.name[0].toUpperCase() + apk.names.name.substring(1); | ||||
|     return App( | ||||
|         currentApp?.id ?? | ||||
|             source.tryInferringAppId(standardUrl, | ||||
|                 additionalSettings: additionalSettings) ?? | ||||
|             ((!source.appIdInferIsOptional || | ||||
|                     (source.appIdInferIsOptional && inferAppIdIfOptional)) | ||||
|                 ? await source.tryInferringAppId(standardUrl, | ||||
|                     additionalSettings: additionalSettings) | ||||
|                 : null) ?? | ||||
|             generateTempID(standardUrl, additionalSettings), | ||||
|         standardUrl, | ||||
|         apk.names.author[0].toUpperCase() + apk.names.author.substring(1), | ||||
| @@ -610,7 +618,11 @@ class SourceProvider { | ||||
|         categories: currentApp?.categories ?? const [], | ||||
|         releaseDate: apk.releaseDate, | ||||
|         changeLog: apk.changeLog, | ||||
|         overrideSource: overrideSource ?? currentApp?.overrideSource); | ||||
|         overrideSource: overrideSource ?? currentApp?.overrideSource, | ||||
|         allowIdChange: currentApp?.allowIdChange ?? | ||||
|             source.appIdInferIsOptional && | ||||
|                 inferAppIdIfOptional // Optional ID inferring may be incorrect - allow correction on first install | ||||
|         ); | ||||
|   } | ||||
|  | ||||
|   // Returns errors in [results, errors] instead of throwing them | ||||
|   | ||||
							
								
								
									
										266
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										266
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -5,18 +5,18 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: android_alarm_manager_plus | ||||
|       sha256: "88a8001851fdc9bd54fa4e30d0277bb900a50f3d86ff244da7f027400bf23ac0" | ||||
|       sha256: "80f963d47cb7ab0818144c7b0668aea4c038f9cb8626626e89a4ea77375defb7" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.4" | ||||
|     version: "3.0.1" | ||||
|   android_intent_plus: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: android_intent_plus | ||||
|       sha256: "04cbc7c332a6f0bba88fed354de78813e9d24049c1800aaf10f449c7adc22603" | ||||
|       sha256: "2c87d8330ba5deef5fe20e77f4d178190b3b24531dce08368030ab4be40a9d4e" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.1.9" | ||||
|     version: "4.0.1" | ||||
|   android_package_installer: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -46,18 +46,18 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: args | ||||
|       sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a | ||||
|       sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.4.1" | ||||
|     version: "2.4.2" | ||||
|   async: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: async | ||||
|       sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 | ||||
|       sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.10.0" | ||||
|     version: "2.11.0" | ||||
|   boolean_selector: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -70,10 +70,26 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: characters | ||||
|       sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c | ||||
|       sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.2.1" | ||||
|     version: "1.3.0" | ||||
|   checked_yaml: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: checked_yaml | ||||
|       sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.3" | ||||
|   cli_util: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: cli_util | ||||
|       sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.4.0" | ||||
|   clock: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -86,10 +102,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: collection | ||||
|       sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 | ||||
|       sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.17.0" | ||||
|     version: "1.17.1" | ||||
|   convert: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -118,10 +134,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: csslib | ||||
|       sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745 | ||||
|       sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.17.2" | ||||
|     version: "1.0.0" | ||||
|   cupertino_icons: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -142,10 +158,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: device_info_plus | ||||
|       sha256: f52ab3b76b36ede4d135aab80194df8925b553686f0fa12226b4e2d658e45903 | ||||
|       sha256: "2c35b6d1682b028e42d07b3aee4b98fa62996c10bc12cb651ec856a80d6a761b" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "8.2.2" | ||||
|     version: "9.0.2" | ||||
|   device_info_plus_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -158,18 +174,18 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: dynamic_color | ||||
|       sha256: bbebb1b7ebed819e0ec83d4abdc2a8482d934f6a85289ffc1c6acf7589fa2aad | ||||
|       sha256: "74dff1435a695887ca64899b8990004f8d1232b0e84bfc4faa1fdda7c6f57cc1" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.6.3" | ||||
|     version: "1.6.5" | ||||
|   easy_localization: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: easy_localization | ||||
|       sha256: "6a2e99fa0bfe5765bf4c6ca9b137d5de2c75593007178c5e4cd2ae985f870080" | ||||
|       sha256: "30ebf25448ffe169e0bd9bc4b5da94faa8398967a2ad2ca09f438be8b6953645" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.0.1" | ||||
|     version: "3.0.2" | ||||
|   easy_logger: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -190,10 +206,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: ffi | ||||
|       sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 | ||||
|       sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.1" | ||||
|     version: "2.0.2" | ||||
|   file: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -206,10 +222,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: file_picker | ||||
|       sha256: b85eb92b175767fdaa0c543bf3b0d1f610fe966412ea72845fe5ba7801e763ff | ||||
|       sha256: b1729fc96627dd44012d0a901558177418818d6bd428df59dcfeb594e5f66432 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "5.2.10" | ||||
|     version: "5.3.2" | ||||
|   flutter: | ||||
|     dependency: "direct main" | ||||
|     description: flutter | ||||
| @@ -223,6 +239,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.2.2" | ||||
|   flutter_launcher_icons: | ||||
|     dependency: "direct dev" | ||||
|     description: | ||||
|       name: flutter_launcher_icons | ||||
|       sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.13.1" | ||||
|   flutter_lints: | ||||
|     dependency: "direct dev" | ||||
|     description: | ||||
| @@ -235,26 +259,26 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_local_notifications | ||||
|       sha256: "2876372952b65ca7f684e698eba22bda1cf581fa071dd30ba2f01900f507d0d1" | ||||
|       sha256: "812791d43ccfc1b443a0d39fa02a206fc228c597e28ff9337e09e3ca8d370391" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "14.0.0+1" | ||||
|     version: "14.1.1" | ||||
|   flutter_local_notifications_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: flutter_local_notifications_linux | ||||
|       sha256: "909bb95de05a2e793503a2437146285a2f600cd0b3f826e26b870a334d8586d7" | ||||
|       sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.0.0" | ||||
|     version: "4.0.0+1" | ||||
|   flutter_local_notifications_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: flutter_local_notifications_platform_interface | ||||
|       sha256: "63235c42de5b6c99846969a27ad0209c401e6b77b0498939813725b5791c107c" | ||||
|       sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "7.0.0" | ||||
|     version: "7.0.0+1" | ||||
|   flutter_localizations: | ||||
|     dependency: transitive | ||||
|     description: flutter | ||||
| @@ -264,18 +288,18 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_markdown | ||||
|       sha256: "7b25c10de1fea883f3c4f9b8389506b54053cd00807beab69fd65c8653a2711f" | ||||
|       sha256: dc6d5258653f6857135b32896ccda7f7af0c54dcec832495ad6835154c6c77c0 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.6.14" | ||||
|     version: "0.6.15" | ||||
|   flutter_plugin_android_lifecycle: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: flutter_plugin_android_lifecycle | ||||
|       sha256: "96af49aa6b57c10a312106ad6f71deed5a754029c24789bbf620ba784f0bd0b0" | ||||
|       sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.14" | ||||
|     version: "2.0.15" | ||||
|   flutter_test: | ||||
|     dependency: "direct dev" | ||||
|     description: flutter | ||||
| @@ -290,26 +314,26 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: fluttertoast | ||||
|       sha256: "2f9c4d3f4836421f7067a28f8939814597b27614e021da9d63e5d3fb6e212d25" | ||||
|       sha256: "474f7d506230897a3cd28c965ec21c5328ae5605fc9c400cd330e9e9d6ac175c" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "8.2.1" | ||||
|     version: "8.2.2" | ||||
|   html: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: html | ||||
|       sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8" | ||||
|       sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.15.3" | ||||
|     version: "0.15.4" | ||||
|   http: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: http | ||||
|       sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" | ||||
|       sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.13.6" | ||||
|     version: "1.0.0" | ||||
|   http_parser: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -318,6 +342,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.0.2" | ||||
|   image: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image | ||||
|       sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.0.17" | ||||
|   installed_apps: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -330,26 +362,34 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: intl | ||||
|       sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" | ||||
|       sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.17.0" | ||||
|     version: "0.18.0" | ||||
|   js: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: js | ||||
|       sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" | ||||
|       sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.6.5" | ||||
|     version: "0.6.7" | ||||
|   json_annotation: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: json_annotation | ||||
|       sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.8.1" | ||||
|   lints: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: lints | ||||
|       sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" | ||||
|       sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.1" | ||||
|     version: "2.1.1" | ||||
|   markdown: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -362,10 +402,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: matcher | ||||
|       sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" | ||||
|       sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.12.13" | ||||
|     version: "0.12.15" | ||||
|   material_color_utilities: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -378,10 +418,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: meta | ||||
|       sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" | ||||
|       sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.8.0" | ||||
|     version: "1.9.1" | ||||
|   mime: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -418,18 +458,18 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: path | ||||
|       sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b | ||||
|       sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.8.2" | ||||
|     version: "1.8.3" | ||||
|   path_provider: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: path_provider | ||||
|       sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4 | ||||
|       sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.14" | ||||
|     version: "2.0.15" | ||||
|   path_provider_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -442,18 +482,18 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: path_provider_foundation | ||||
|       sha256: ad4c4d011830462633f03eb34445a45345673dfd4faf1ab0b4735fbd93b19183 | ||||
|       sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.2.2" | ||||
|     version: "2.2.3" | ||||
|   path_provider_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: path_provider_linux | ||||
|       sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" | ||||
|       sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.10" | ||||
|     version: "2.1.11" | ||||
|   path_provider_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -466,42 +506,42 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: path_provider_windows | ||||
|       sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 | ||||
|       sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.6" | ||||
|     version: "2.1.7" | ||||
|   permission_handler: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: permission_handler | ||||
|       sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" | ||||
|       sha256: "1b6b3e73f0bcbc856548bbdfb1c33084a401c4f143e220629a9055233d76c331" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "10.2.0" | ||||
|     version: "10.3.0" | ||||
|   permission_handler_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: permission_handler_android | ||||
|       sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2" | ||||
|       sha256: "8f6a95ccbca13766882f95d32684d7c9bfe6c45650c32bedba948ef1c6a4ddf7" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "10.2.0" | ||||
|     version: "10.2.3" | ||||
|   permission_handler_apple: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: permission_handler_apple | ||||
|       sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85 | ||||
|       sha256: "08dcb6ce628ac0b257e429944b4c652c2a4e6af725bdf12b498daa2c6b2b1edb" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "9.0.8" | ||||
|     version: "9.1.0" | ||||
|   permission_handler_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: permission_handler_platform_interface | ||||
|       sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" | ||||
|       sha256: de20a5c3269229c1ae2e5a6b822f6cb59578b23e8255c93fbeebfc82116e6b11 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.9.0" | ||||
|     version: "3.10.0" | ||||
|   permission_handler_windows: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -514,10 +554,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: petitparser | ||||
|       sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" | ||||
|       sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "5.1.0" | ||||
|     version: "5.4.0" | ||||
|   platform: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -562,10 +602,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: share_plus | ||||
|       sha256: b1f15232d41e9701ab2f04181f21610c36c83a12ae426b79b4bd011c567934b1 | ||||
|       sha256: ed3fcea4f789ed95913328e629c0c53e69e80e08b6c24542f1b3576046c614e8 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.3.4" | ||||
|     version: "7.0.2" | ||||
|   share_plus_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -578,10 +618,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: shared_preferences | ||||
|       sha256: "858aaa72d8f61637d64e776aca82e1c67e6d9ee07979123c5d17115031c1b13b" | ||||
|       sha256: "396f85b8afc6865182610c0a2fc470853d56499f75f7499e2a73a9f0539d23d0" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.0" | ||||
|     version: "2.1.2" | ||||
|   shared_preferences_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -594,10 +634,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shared_preferences_foundation | ||||
|       sha256: "0c1c16c56c9708aa9c361541a6f0e5cc6fc12a3232d866a687a7b7db30032b07" | ||||
|       sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.2.1" | ||||
|     version: "2.2.2" | ||||
|   shared_preferences_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -647,18 +687,18 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: sqflite | ||||
|       sha256: acf091c6e55c50d00b30b8532b2dd23e393cf775861665ebd0f15cdd6ebfb079 | ||||
|       sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.2.8+1" | ||||
|     version: "2.2.8+4" | ||||
|   sqflite_common: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: sqflite_common | ||||
|       sha256: e77abf6ff961d69dfef41daccbb66b51e9983cdd5cb35bf30733598057401555 | ||||
|       sha256: "8f7603f3f8f126740bc55c4ca2d1027aab4b74a1267a3e31ce51fe40e3b65b8f" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.4.5" | ||||
|     version: "2.4.5+1" | ||||
|   stack_trace: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -703,10 +743,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: test_api | ||||
|       sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 | ||||
|       sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.4.16" | ||||
|     version: "0.5.1" | ||||
|   timezone: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -719,26 +759,26 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: typed_data | ||||
|       sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" | ||||
|       sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.3.1" | ||||
|     version: "1.3.2" | ||||
|   url_launcher: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: url_launcher | ||||
|       sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" | ||||
|       sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.1.10" | ||||
|     version: "6.1.11" | ||||
|   url_launcher_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: url_launcher_android | ||||
|       sha256: "22f8db4a72be26e9e3a4aa3f194b1f7afbc76d20ec141f84be1d787db2155cbd" | ||||
|       sha256: "15f5acbf0dce90146a0f5a2c4a002b1814a6303c4c5c075aa2623b2d16156f03" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.0.31" | ||||
|     version: "6.0.36" | ||||
|   url_launcher_ios: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -767,18 +807,18 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: url_launcher_platform_interface | ||||
|       sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" | ||||
|       sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.2" | ||||
|     version: "2.1.3" | ||||
|   url_launcher_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: url_launcher_web | ||||
|       sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa" | ||||
|       sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.16" | ||||
|     version: "2.0.17" | ||||
|   url_launcher_windows: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -807,42 +847,50 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: webview_flutter | ||||
|       sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567" | ||||
|       sha256: "789d52bd789373cc1e100fb634af2127e86c99cf9abde09499743270c5de8d00" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.2.0" | ||||
|     version: "4.2.2" | ||||
|   webview_flutter_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_android | ||||
|       sha256: "1acea8def62592123e2fbbca164ed8681a98a890bdcbb88f916d5b4a22687759" | ||||
|       sha256: "532135f6f6b8030cd039f30eab23f340d650350e29f38e9b37d2eaad028f1018" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.7.0" | ||||
|     version: "3.8.0" | ||||
|   webview_flutter_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_platform_interface | ||||
|       sha256: "78715dc442b7849dbde74e92bb67de1cecf5addf95531c5fb474e72f5fe9a507" | ||||
|       sha256: "656e2aeaef318900fffd21468b6ddc7958c7092a642f0e7220bac328b70d4a81" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.3.0" | ||||
|     version: "2.3.1" | ||||
|   webview_flutter_wkwebview: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_wkwebview | ||||
|       sha256: "61f33512810bf1ee9ac89761a4b02663ff64e8227b7dc80654642acd660fd49d" | ||||
|       sha256: ecc9e9ea15216afc5ba3b1f14aa19414ceba526e57b19cebd970bfa91a0f4058 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.4.2" | ||||
|     version: "3.5.0" | ||||
|   win32: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: win32 | ||||
|       sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 | ||||
|       sha256: "1414f27dd781737e51afa9711f2ac2ace6ab4498ee98e20863fa5505aa00c58c" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.1.4" | ||||
|     version: "5.0.4" | ||||
|   win32_registry: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: win32_registry | ||||
|       sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.1" | ||||
|   xdg_directories: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -855,10 +903,18 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: xml | ||||
|       sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" | ||||
|       sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.2.2" | ||||
|     version: "6.3.0" | ||||
|   yaml: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: yaml | ||||
|       sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.1.2" | ||||
| sdks: | ||||
|   dart: ">=2.19.0 <3.0.0" | ||||
|   dart: ">=3.0.0 <4.0.0" | ||||
|   flutter: ">=3.4.0-17.0.pre" | ||||
|   | ||||
							
								
								
									
										17
									
								
								pubspec.yaml
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								pubspec.yaml
									
									
									
									
									
								
							| @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev | ||||
| # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html | ||||
| # In Windows, build-name is used as the major, minor, and patch parts | ||||
| # of the product and file versions while build-number is used as the build suffix. | ||||
| version: 0.13.1+165 # When changing this, update the tag in main() accordingly | ||||
| version: 0.13.11+175 # When changing this, update the tag in main() accordingly | ||||
|  | ||||
| environment: | ||||
|   sdk: '>=2.18.2 <3.0.0' | ||||
| @@ -40,7 +40,7 @@ dependencies: | ||||
|   flutter_fgbg: ^0.2.0 # Try removing reliance on this | ||||
|   flutter_local_notifications: ^14.0.0+1 | ||||
|   provider: ^6.0.3 | ||||
|   http: ^0.13.5 | ||||
|   http: ^1.0.0 | ||||
|   webview_flutter: ^4.0.0 | ||||
|   dynamic_color: ^1.5.4 | ||||
|   html: ^0.15.0 | ||||
| @@ -48,20 +48,20 @@ dependencies: | ||||
|   url_launcher: ^6.1.5 | ||||
|   permission_handler: ^10.0.0 | ||||
|   fluttertoast: ^8.0.9 | ||||
|   device_info_plus: ^8.0.0 | ||||
|   device_info_plus: ^9.0.0 | ||||
|   file_picker: ^5.2.10 | ||||
|   animations: ^2.0.4 | ||||
|   android_package_installer: | ||||
|     git: | ||||
|       url: https://github.com/ImranR98/android_package_installer | ||||
|       ref: main | ||||
|   share_plus: ^6.0.1 | ||||
|   share_plus: ^7.0.0 | ||||
|   installed_apps: ^1.3.1 | ||||
|   package_archive_info: ^0.1.0 | ||||
|   android_alarm_manager_plus: ^2.1.0 | ||||
|   android_alarm_manager_plus: ^3.0.0 | ||||
|   sqflite: ^2.2.0+3 | ||||
|   easy_localization: ^3.0.1 | ||||
|   android_intent_plus: ^3.1.5 | ||||
|   android_intent_plus: ^4.0.0 | ||||
|   flutter_markdown: ^0.6.14 | ||||
|   archive: ^3.3.7 | ||||
|  | ||||
| @@ -69,6 +69,7 @@ dependencies: | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|     sdk: flutter | ||||
|   flutter_launcher_icons: ^0.13.1 | ||||
|  | ||||
|   # The "flutter_lints" package below contains a set of recommended lints to | ||||
|   # encourage good coding practices. The lint set provided by the package is | ||||
| @@ -77,6 +78,10 @@ dev_dependencies: | ||||
|   # rules and activating additional ones. | ||||
|   flutter_lints: ^2.0.1 | ||||
|  | ||||
| flutter_launcher_icons: | ||||
|   android: "ic_launcher" | ||||
|   image_path: "assets/graphics/icon.png" | ||||
|  | ||||
| # For information on the generic Dart part of this file, see the | ||||
| # following page: https://dart.dev/tools/pub/pubspec | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user