mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-31 13:33:28 +01:00 
			
		
		
		
	Compare commits
	
		
			37 Commits
		
	
	
		
			v0.13.18-b
			...
			v0.13.21-b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | d9225fd639 | ||
|  | 995d44551c | ||
|  | 68c0224b98 | ||
|  | 7767468d5d | ||
|  | f9f83d8243 | ||
|  | cc4cec829f | ||
|  | f665bf1eb2 | ||
|  | bccd52054e | ||
|  | 3a4c782aab | ||
|  | 6e047e96fa | ||
|  | 42f8753166 | ||
|  | b6a64129b3 | ||
|  | bb5bd23263 | ||
|  | 58361a0493 | ||
|  | 7671ab95f9 | ||
|  | 41f102c0ce | ||
|  | 5cee527d6f | ||
|  | c2eb13d5e5 | ||
|  | ed89e20826 | ||
|  | e50c7554e3 | ||
|  | 00354f10a3 | ||
|  | 031d0b7444 | ||
|  | 2ced06367b | ||
|  | 9ef3279bae | ||
|  | b055486d74 | ||
|  | c70f2c481e | ||
|  | e4b26be01f | ||
|  | b27bdc63c1 | ||
|  | 543e7d8cdc | ||
|  | 852decbe7d | ||
|  | d2150320fa | ||
|  | 9c723c7522 | ||
|  | ac5660de88 | ||
|  | e6467452a6 | ||
|  | 04b49c2e61 | ||
|  | 324773ba58 | ||
|  | d885ca5db7 | 
							
								
								
									
										291
									
								
								assets/translations/bs.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								assets/translations/bs.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,291 @@ | ||||
|  { | ||||
|    "invalidURLForSource": "Nije važeći URL aplikacije {}", | ||||
|    "noReleaseFound": "Nije moguće pronaći odgovarajuće izdanje", | ||||
|    "noVersionFound": "Nije moguće odrediti verziju izdanja", | ||||
|    "urlMatchesNoSource": "URL se ne podudara s poznatim izvorom", | ||||
|    "cantInstallOlderVersion": "Nije moguće instalirati stariju verziju aplikacije", | ||||
|    "appIdMismatch": "ID preuzetog paketa se ne podudara s postojećim ID-om aplikacije", | ||||
|    "functionNotImplemented": "Ova klasa nije implementirala ovu funkciju", | ||||
|    "placeholder": "Rezervirano mjesto", | ||||
|    "someErrors": "Došlo je do nekih grešaka", | ||||
|    "unexpectedError": "Neočekivana greška", | ||||
|    "ok": "Dobro", | ||||
|    "and": "i", | ||||
|    "startedBgUpdateTask": "Započeo je pozadinski zadatak provjere ažuriranja", | ||||
|    "bgUpdateIgnoreAfterIs": "ignoreAfter pozadinskog zadataka je  {}", | ||||
|    "startedActualBGUpdateCheck": "Započela je stvarna provjera ažuriranja", | ||||
|    "bgUpdateTaskFinished": "Završen zadatak provjere ažuriranja", | ||||
|    "firstRun": "Ovo je prvi put da pokrećete Obtainium", | ||||
|    "settingUpdateCheckIntervalTo": "Podešavanje intervala ažuriranja na {}", | ||||
|    "githubPATLabel": "GitHub token za lični pristup (eng. PAT, povećava ograničenje stope)", | ||||
|    "githubPATHint": "PAT mora biti u ovom formatu: korisničko_ime:token", | ||||
|    "githubPATFormat": "korisničko_ime:token", | ||||
|    "includePrereleases": "Uključi preliminarna izdanja", | ||||
|    "fallbackToOlderReleases": "Povratak na starija izdanja", | ||||
|    "filterReleaseTitlesByRegEx": "Filtrirajte naslove izdanja prema regularnom izrazu", | ||||
|    "invalidRegEx": "Nevažeći regularni izraz", | ||||
|    "noDescription": "Bez opisa", | ||||
|    "cancel": "Otkaži", | ||||
|    "continue": "Nastavite", | ||||
|    "requiredInBrackets": "(obavezno)", | ||||
|    "dropdownNoOptsError": "GREŠKA: PADAJUĆI MENI MORA IMATI NAJMANJE JEDNU OPCIJU", | ||||
|    "colour": "Boja", | ||||
|    "githubStarredRepos": "GitHub repo-i sa zvjezdicom", | ||||
|    "uname": "Korisničko ime", | ||||
|    "wrongArgNum": "Naveden je pogrešan broj argumenata", | ||||
|    "xIsTrackOnly": "{} je samo za praćenje", | ||||
|    "source": "Izvor", | ||||
|    "app": "Aplikacija. ", | ||||
|    "appsFromSourceAreTrackOnly": "Aplikacije iz ovog izvora su 'Samo za praćenje'.", | ||||
|    "youPickedTrackOnly": "Odabrali ste opciju „Samo za praćenje”.", | ||||
|    "trackOnlyAppDescription": "Aplikacija će se pratiti radi ažuriranja, ali Obtainium neće moći da je preuzme ili instalira.", | ||||
|    "cancelled": "Otkazano", | ||||
|    "appAlreadyAdded": "Aplikacija je već dodana", | ||||
|    "alreadyUpToDateQuestion": "Aplikacija je već ažurirana?", | ||||
|    "addApp": "Dodaj aplikaciju", | ||||
|    "appSourceURL": "Izvorni URL aplikacije", | ||||
|    "error": "Greška", | ||||
|    "add": "Dodaj", | ||||
|    "searchSomeSourcesLabel": "Pretraživanje (samo neki izvori)", | ||||
|    "search": "Pretraživanje", | ||||
|    "additionalOptsFor": "Dodatne opcije za {}", | ||||
|    "supportedSourcesBelow": "Podržani izvori:", | ||||
|    "trackOnlyInBrackets": "(Samo za praćenje)", | ||||
|    "searchableInBrackets": "(Može se pretraživati)", | ||||
|    "appsString": "Aplikacije", | ||||
|    "noApps": "Nema aplikacija", | ||||
|    "noAppsForFilter": "Nema aplikacija za filter", | ||||
|    "byX": "Autor {}", | ||||
|    "percentProgress": "Napredak: {}%", | ||||
|    "pleaseWait": "Molimo sačekajte", | ||||
|    "updateAvailable": "Ažuriranje dostupno", | ||||
|    "estimateInBracketsShort": "(Procjena)", | ||||
|    "notInstalled": "Nije instalirano", | ||||
|    "estimateInBrackets": "(Procjena)", | ||||
|    "selectAll": "Označi sve", | ||||
|    "deselectN": "Poništi odabir {}", | ||||
|    "xWillBeRemovedButRemainInstalled": "{} će biti uklonjen iz Obtainiuma, ali će ostati instaliran na uređaju.", | ||||
|    "removeSelectedAppsQuestion": "Želite li ukloniti odabrane aplikacije?", | ||||
|    "removeSelectedApps": "Ukloni odabrane aplikacije", | ||||
|    "updateX": "Nadogradi {}", | ||||
|    "installX": "Instaliraj {}", | ||||
|    "markXTrackOnlyAsUpdated": "Označi {}\n(samo za praćenje)\nkao ažurirano", | ||||
|    "changeX": "Promjena {}", | ||||
|    "installUpdateApps": "Instalirajte/ažurirajte aplikacije", | ||||
|    "installUpdateSelectedApps": "Instalirajte/ažurirajte odabrane aplikacije", | ||||
|    "markXSelectedAppsAsUpdated": "Označite {} odabrane aplikacije kao ažurirane?", | ||||
|    "no": "Ne", | ||||
|    "yes": "Da", | ||||
|    "markSelectedAppsUpdated": "Označi odabrane aplikacije kao ažurirane", | ||||
|    "pinToTop": "Prikvači na vrh", | ||||
|    "unpinFromTop": "Otkvači sa vrha", | ||||
|    "resetInstallStatusForSelectedAppsQuestion": "Resetujte status instalacije za odabrane aplikacije?", | ||||
|    "installStatusOfXWillBeResetExplanation": "Status instalacije bilo koje odabrane aplikacije će se resetovati.\n\nTo može pomoći kada je verzija aplikacije prikazana u Obtainiumu netačna zbog neuspjelih ažuriranja ili drugih problema.", | ||||
|    "shareSelectedAppURLs": "Podijeli odabrane URL-ove aplikacija", | ||||
|    "resetInstallStatus": "Resetujte status instalacije", | ||||
|    "more": "Više", | ||||
|    "removeOutdatedFilter": "Uklonite zastarjeli filter aplikacija", | ||||
|    "showOutdatedOnly": "Prikaži samo zastarjele aplikacije", | ||||
|    "filter": "Filtriranje", | ||||
|    "filterActive": "Filtriranje", | ||||
|    "filterApps": "Filtriraj aplikacije", | ||||
|    "appName": "Naziv aplikacije", | ||||
|    "author": "Autor", | ||||
|    "upToDateApps": "Ažurirane aplikacije", | ||||
|    "nonInstalledApps": "Neinstalirane aplikacije", | ||||
|    "importExport": "Uvoz/izvoz", | ||||
|    "settings": "Postavke", | ||||
|    "exportedTo": "Izvezeno u {}", | ||||
|    "obtainiumExport": "Obtainium Export", | ||||
|    "invalidInput": "Neispravan unos.", | ||||
|    "importedX": "Uvezeno {}", | ||||
|    "obtainiumImport": "Obtainium uvoz", | ||||
|    "importFromURLList": "Uvoz iz URL liste", | ||||
|    "searchQuery": "Pretraga za: ", | ||||
|    "appURLList": "Lista URL adresa aplikacija", | ||||
|    "line": "Linija", | ||||
|    "searchX": "Pretraživanje {}", | ||||
|    "noResults": "Nema rezultata", | ||||
|    "importX": "Uvoz {}", | ||||
|    "importedAppsIdDisclaimer": "Uvezene aplikacije mogu se pogrešno prikazati kao „Nije instalirano”.\nDa biste to riješili, ponovo ih instalirajte putem aplikacije Obtainium.\nTo ne bi trebalo uticati na podatke aplikacije.\n\nUtječe samo na URL i metode uvoza treće strane.", | ||||
|    "importErrors": "Uvezi greške", | ||||
|    "importedXOfYApps": "{} od {} aplikacija uvezeno.", | ||||
|    "followingURLsHadErrors": "Sljedeći URL-ovi su imali greške:", | ||||
|    "okay": "Dobro", | ||||
|    "selectURL": "Odaberite URL", | ||||
|    "selectURLs": "Odaberite URL-ove", | ||||
|    "pick": "Odaberi", | ||||
|    "theme": "Tema", | ||||
|    "dark": "Tamna", | ||||
|    "light": "Svijetla", | ||||
|    "followSystem": "Pratite sistem", | ||||
|    "obtainium": "Obtainium", | ||||
|    "materialYou": "Material You", | ||||
|    "useBlackTheme": "Koristite čisto crnu tamnu temu", | ||||
|    "appSortBy": "Aplikacije sortirane po", | ||||
|    "authorName": "Autor/Ime", | ||||
|    "nameAuthor": "Ime/Autor", | ||||
|    "asAdded": "Kao što je dodano", | ||||
|    "appSortOrder": "Redoslijed sortiranja aplikacija", | ||||
|    "ascending": "Uzlazno", | ||||
|    "descending": "Silazno", | ||||
|    "bgUpdateCheckInterval": "Interval provjere ažuriranja u pozadini", | ||||
|    "neverManualOnly": "Nikada - samo ručno", | ||||
|    "appearance": "Izgled", | ||||
|    "showWebInAppView": "Prikaži izvornu web stranicu u prikazu aplikacije", | ||||
|    "pinUpdates": "Prikvačite ažuriranja na vrh prikaza aplikacija", | ||||
|    "updates": "Nadogradnje", | ||||
|    "sourceSpecific": "Specifično za izvor", | ||||
|    "appSource": "Izvor aplikacije", | ||||
|    "noLogs": "Nema evidencije", | ||||
|    "appLogs": "Evidencije aplikacija", | ||||
|    "close": "Zatvori", | ||||
|    "share": "Podijeli", | ||||
|    "appNotFound": "Aplikacija nije pronađena", | ||||
|    "obtainiumExportHyphenatedLowercase": "obtainium-export", | ||||
|    "pickAnAPK": "Odaberite APK", | ||||
|    "appHasMoreThanOnePackage": "{} ima više od jednog paketa:", | ||||
|    "deviceSupportsXArch": "Vaš uređaj podržava {} arhitekturu procesora.", | ||||
|    "deviceSupportsFollowingArchs": "Vaš uređaj podržava sljedeće arhitekture procesora:", | ||||
|    "warning": "Upozorenje", | ||||
|    "sourceIsXButPackageFromYPrompt": "Izvor aplikacije je '{}', ali paket za izdavanje dolazi iz '{}'. Želite li nastaviti?", | ||||
|    "updatesAvailable": "Dostupna ažuriranja", | ||||
|    "updatesAvailableNotifDescription": "Obavještava korisnika da su ažuriranja dostupna za jednu ili više aplikacija koje prati Obtainium", | ||||
|    "noNewUpdates": "Nema novih ažuriranja.", | ||||
|    "xHasAnUpdate": "{} ima ažuriranje.", | ||||
|    "appsUpdated": "Aplikacije su ažurirane", | ||||
|    "appsUpdatedNotifDescription": "Obavještava korisnika da su u pozadini primijenjena ažuriranja na jednu ili više aplikacija", | ||||
|    "xWasUpdatedToY": "{} je ažuriran na {}.", | ||||
|    "errorCheckingUpdates": "Greška pri provjeri ažuriranja", | ||||
|    "errorCheckingUpdatesNotifDescription": "Obavijest koja se prikazuje kada provjera sigurnosnog ažuriranja ne uspije", | ||||
|    "appsRemoved": "Aplikacije su uklonjene", | ||||
|    "appsRemovedNotifDescription": "Obavještava korisnika da je jedna ili više aplikacija uklonjeno zbog grešaka prilikom učitavanja", | ||||
|    "xWasRemovedDueToErrorY": "{} je uklonjen zbog ove greške: {}", | ||||
|    "completeAppInstallation": "Dovršite instalaciju aplikacije", | ||||
|    "obtainiumMustBeOpenToInstallApps": "Obtainium mora biti otvoren za instalaciju aplikacija", | ||||
|    "completeAppInstallationNotifDescription": "Traži od korisnika da se vrati u Obtainium kako bi dovršio instalaciju aplikacije", | ||||
|    "checkingForUpdates": "Tražim moguće nadogradnje", | ||||
|    "checkingForUpdatesNotifDescription": "Privremeno obavještenje koje se pojavljuje prilikom provjere ažuriranja", | ||||
|    "pleaseAllowInstallPerm": "Dozvolite Obtainiumu da instalira aplikacije", | ||||
|    "trackOnly": "Samo za praćenje", | ||||
|    "errorWithHttpStatusCode": "Greška {}", | ||||
|    "versionCorrectionDisabled": "Ispravka verzije je onemogućena (izgleda da plugin ne radi)", | ||||
|    "unknown": "Nepoznato", | ||||
|    "none": "Ništa", | ||||
|    "never": "Nikad", | ||||
|    "latestVersionX": "Najnovija verzija: {}", | ||||
|    "installedVersionX": "Instalirana verzija: {}", | ||||
|    "lastUpdateCheckX": "Posljednja provjera ažuriranja: {}", | ||||
|    "remove": "Izbriši", | ||||
|    "yesMarkUpdated": "Da, označi kao ažurirano", | ||||
|    "fdroid": "F-Droid Official", | ||||
|    "appIdOrName": "ID ili ime aplikacije", | ||||
|    "appId": "Apl ID", | ||||
|    "appWithIdOrNameNotFound": "Nije pronađena aplikacija s tim ID-om ili imenom", | ||||
|    "reposHaveMultipleApps": "Repo-i mogu sadržavati više aplikacija", | ||||
|    "fdroidThirdPartyRepo": "F-Droid Repo treće strane", | ||||
|    "steam": "Steam", | ||||
|    "steamMobile": "Steam Mobile", | ||||
|    "steamChat": "Razgovor na Steamu (chat)", | ||||
|    "install": "Instaliraj", | ||||
|    "markInstalled": "Označi kao instalirano", | ||||
|    "update": "Nadogradi", | ||||
|    "markUpdated": "Označi kao ažurirano", | ||||
|    "additionalOptions": "Dodatne opcije", | ||||
|    "disableVersionDetection": "Onemogući detekciju verzije", | ||||
|    "noVersionDetectionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno.", | ||||
|    "downloadingX": "Preuzimanje {}", | ||||
|    "downloadNotifDescription": "Obavještava korisnika o napretku u preuzimanju aplikacije", | ||||
|    "noAPKFound": "APK nije pronađen", | ||||
|    "noVersionDetection": "Nema detekcije verzije", | ||||
|    "categorize": "Kategoriziraj", | ||||
|    "categories": "Kategorije", | ||||
|    "category": "Kategorija", | ||||
|    "noCategory": "Nema kategorije", | ||||
|    "noCategories": "Nema kategorija", | ||||
|    "deleteCategoriesQuestion": "Želite li izbrisati kategorije?", | ||||
|    "categoryDeleteWarning": "Sve aplikacije u izbrisanim kategorijama će biti postavljene kao nekategorisane.", | ||||
|    "addCategory": "Dodaj kategoriju", | ||||
|    "label": "Oznaka", | ||||
|    "language": "Jezik", | ||||
|    "copiedToClipboard": "Podaci kopirani u međuspremnik", | ||||
|    "storagePermissionDenied": "Dozvola za pohranu je odbijena", | ||||
|    "selectedCategorizeWarning": "Ovo će zamijeniti sve postojeće postavke kategorije za odabrane aplikacije.", | ||||
|    "filterAPKsByRegEx": "Filtrirajte APK-ove prema regularnom izrazu", | ||||
|    "removeFromObtainium": "Ukloni iz Obtainiuma", | ||||
|    "uninstallFromDevice": "Deinstaliraj s uređaja", | ||||
|    "onlyWorksWithNonVersionDetectApps": "Radi samo za aplikacije s onemogućenom detekcijom verzije.", | ||||
|    "releaseDateAsVersion": "Koristi datum izdanja kao verziju", | ||||
|    "releaseDateAsVersionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno, ali je datum izdavanja dostupan.", | ||||
|    "changes": "Promjene", | ||||
|    "releaseDate": "Datum izdavanja", | ||||
|    "importFromURLsInFile": "Uvoz iz URL-ova u datoteci (kao što je OPML)", | ||||
|    "versionDetection": "Otkrivanje verzije", | ||||
|    "standardVersionDetection": "Detekcija standardne verzije", | ||||
|    "groupByCategory": "Grupiši po kategoriji", | ||||
|    "autoApkFilterByArch": "Pokušajte filtrirati APK-ove po arhitekturi procesora ako je moguće", | ||||
|    "overrideSource": "Premosti izvor", | ||||
|    "dontShowAgain": "Ne prikazuj ovo ponovo", | ||||
|    "dontShowTrackOnlyWarnings": "Ne prikazuj upozorenja „Samo za  praćenje”", | ||||
|    "dontShowAPKOriginWarnings": "Ne prikazuj upozorenja o porijeklu APK-a", | ||||
|    "moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija", | ||||
|    "gitlabPATLabel": "GitLab token za lični pristup\n(Omogućava pretraživanje i bolje otkrivanje APK-a)", | ||||
|    "about": "O nama", | ||||
|    "requiresCredentialsInSettings": "Za ovo su potrebni dodatni akreditivi (u Postavkama)", | ||||
|    "checkOnStart": "Provjerite ima li novosti pri pokretanju", | ||||
|    "tryInferAppIdFromCode": "Pokušati otkriti ID aplikacije iz izvornog koda", | ||||
|    "removeOnExternalUninstall": "Automatski ukloni eksterno deinstalirane aplikacije", | ||||
|    "pickHighestVersionCode": "Automatski odaberite najviši kôd verzije APK-a", | ||||
|    "checkUpdateOnDetailPage": "Provjerite ima li novosti pri otvaranju stranice s detaljima aplikacije", | ||||
|    "disablePageTransitions": "Ugasite animaciju prijelaza stranice", | ||||
|    "reversePageTransitions": "Reverzne animacije prijelaza stranice", | ||||
|    "minStarCount": "Minimum Star Count", | ||||
|    "removeAppQuestion": { | ||||
|       "one": "Želite li ukloniti aplikaciju?", | ||||
|       "other": "Želite li ukloniti aplikacije?" | ||||
|    }, | ||||
|    "tooManyRequestsTryAgainInMinutes": { | ||||
|       "one": "Previše zahtjeva (ograničena broj zahteva) - pokušajte ponovo za {} minutu", | ||||
|       "other": "Previše zahtjeva (ograničena cijena) - pokušajte ponovo za {} min." | ||||
|    }, | ||||
|    "bgUpdateGotErrorRetryInMinutes": { | ||||
|       "one": "Provjera ažuriranja u pozadini naišla je na {}, zakazuje se ponovni pokušaj za {} minutu", | ||||
|       "other": "Provjera ažuriranja u pozadini naišla je na {}, zakazuje se ponovni pokušaj za {} min." | ||||
|    }, | ||||
|    "bgCheckFoundUpdatesWillNotifyIfNeeded": { | ||||
|       "one": "Provjera ažuriranja u pozadini je pronašla {} ažuriranje - korisnik će biti obavješten ako je to potrebno", | ||||
|       "other": "Provjera ažuriranja u pozadini je pronašla {} ažuriranja - korisnik će biti obavješten ako je to potrebno" | ||||
|    }, | ||||
|    "apps": { | ||||
|       "one": "{} aplikacija", | ||||
|       "other": "{} aplikacije" | ||||
|    }, | ||||
|    "url": { | ||||
|       "one": "{} URL", | ||||
|       "other": "{} URL-ovi" | ||||
|    }, | ||||
|    "minute": { | ||||
|       "one": "{} minuta", | ||||
|       "other": "min." | ||||
|    }, | ||||
|    "hour": { | ||||
|       "one": "{} sat", | ||||
|       "other": "{} sat/i" | ||||
|    }, | ||||
|    "day": { | ||||
|       "one": "{} dan", | ||||
|       "other": "{} dana" | ||||
|    }, | ||||
|    "clearedNLogsBeforeXAfterY": { | ||||
|       "one": "Izbrisan {n} log (prije = {before}, nakon = {after})", | ||||
|       "other": "Izbrisano {n} log-ova (prije = {before}, nakon = {after})" | ||||
|    }, | ||||
|    "xAndNMoreUpdatesAvailable": { | ||||
|       "one": "{} i još 1 aplikacija ima ažuriranja.", | ||||
|       "other": "{} i još {} aplikacija imaju ažuriranja." | ||||
|    }, | ||||
|    "xAndNMoreUpdatesInstalled": { | ||||
|       "one": "{} i još 1 aplikacija je ažurirana.", | ||||
|       "other": "{} i još {} aplikacija je ažurirano." | ||||
|    } | ||||
| } | ||||
| @@ -239,6 +239,7 @@ | ||||
|     "checkUpdateOnDetailPage": "Check for updates on opening an App detail page", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "minStarCount": "Minimum Star Count", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "App entfernen?", | ||||
|         "other": "Apps entfernen?" | ||||
|   | ||||
| @@ -239,6 +239,7 @@ | ||||
|     "checkUpdateOnDetailPage": "Check for updates on opening an App detail page", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "minStarCount": "Minimum Star Count", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Remove App?", | ||||
|         "other": "Remove Apps?" | ||||
|   | ||||
| @@ -239,6 +239,7 @@ | ||||
|     "checkUpdateOnDetailPage": "Check for updates on opening an App detail page", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "minStarCount": "Minimum Star Count", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "¿Eliminar Aplicación?", | ||||
|         "other": "¿Eliminar Aplicaciones?" | ||||
|   | ||||
| @@ -239,6 +239,7 @@ | ||||
|     "checkUpdateOnDetailPage": "Check for updates on opening an App detail page", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "minStarCount": "Minimum Star Count", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "برنامه حذف شود؟", | ||||
|         "other": "برنامه ها حذف شوند؟" | ||||
|   | ||||
| @@ -239,6 +239,7 @@ | ||||
|     "checkUpdateOnDetailPage": "Check for updates on opening an App detail page", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "minStarCount": "Minimum Star Count", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Supprimer l'application ?", | ||||
|         "other": "Supprimer les applications ?" | ||||
|   | ||||
| @@ -236,8 +236,9 @@ | ||||
|     "removeOnExternalUninstall": "A külsőleg eltávolított appok auto. eltávolítása", | ||||
|     "pickHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása", | ||||
|     "checkUpdateOnDetailPage": "Frissítések keresése az app részleteit tartalmazó oldal megnyitásakor", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "disablePageTransitions": "Lap áttűnési animációk tiltása", | ||||
|     "reversePageTransitions": "Fordított lap áttűnési animációk", | ||||
|     "minStarCount": "Minimális csillag szám", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Eltávolítja az alkalmazást?", | ||||
|         "other": "Eltávolítja az alkalmazást?" | ||||
|   | ||||
| @@ -239,6 +239,7 @@ | ||||
|     "checkUpdateOnDetailPage": "Check for updates on opening an App detail page", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "minStarCount": "Minimum Star Count", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Rimuovere l'app?", | ||||
|         "other": "Rimuovere le app?" | ||||
|   | ||||
| @@ -237,8 +237,9 @@ | ||||
|     "removeOnExternalUninstall": "外部でアンインストールされたアプリを自動的に削除する", | ||||
|     "pickHighestVersionCode": "最も高いバージョンコードのAPKを自動的に選択する", | ||||
|     "checkUpdateOnDetailPage": "アプリの詳細ページを開く際にアップデートを確認する", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "disablePageTransitions": "ページ遷移アニメーションを無効化する", | ||||
|     "reversePageTransitions": "ページ遷移アニメーションを反転する", | ||||
|     "minStarCount": "Minimum Star Count", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "アプリを削除しますか?", | ||||
|         "other": "アプリを削除しますか?" | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|     "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", | ||||
|     "appIdMismatch": "Pobrane ID pakietu nie pasuje do istniejącego ID aplikacji", | ||||
|     "functionNotImplemented": "Ta klasa nie zaimplementowała tej funkcji", | ||||
|     "placeholder": "Placeholder", | ||||
|     "someErrors": "Wystąpiły pewne błędy", | ||||
| @@ -230,7 +230,7 @@ | ||||
|     "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”", | ||||
|     "dontShowTrackOnlyWarnings": "Nie pokazuj 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\n(Umożliwia wyszukiwanie i lepsze wykrywanie APK)", | ||||
| @@ -241,8 +241,9 @@ | ||||
|     "removeOnExternalUninstall": "Automatyczne usuń odinstalowane zewnętrznie aplikacje", | ||||
|     "pickHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK", | ||||
|     "checkUpdateOnDetailPage": "Sprawdzaj aktualizacje podczas otwierania strony szczegółów aplikacji", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "disablePageTransitions": "Wyłącz animacje przejścia między stronami", | ||||
|     "reversePageTransitions": "Odwróć animacje przejścia pomiędzy stronami", | ||||
|     "minStarCount": "Minimum Star Count", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Usunąć aplikację?", | ||||
|         "other": "Usunąć aplikacje?" | ||||
|   | ||||
| @@ -116,9 +116,9 @@ | ||||
|     "selectURLs": "Выбрать URL-адреса", | ||||
|     "pick": "Выбрать", | ||||
|     "theme": "Тема", | ||||
|     "dark": "Темный", | ||||
|     "light": "Светлый", | ||||
|     "followSystem": "Следовать системе", | ||||
|     "dark": "Темная", | ||||
|     "light": "Светлая", | ||||
|     "followSystem": "Как в системе", | ||||
|     "obtainium": "Obtainium", | ||||
|     "materialYou": "Material You", | ||||
|     "useBlackTheme": "Использовать чёрную тему", | ||||
| @@ -183,7 +183,7 @@ | ||||
|     "appId": "ID приложения", | ||||
|     "appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено", | ||||
|     "reposHaveMultipleApps": "В хранилище может быть несколько приложений", | ||||
|     "fdroidThirdPartyRepo": "Репозитории F-Droid сторонних разработчиков", | ||||
|     "fdroidThirdPartyRepo": "Сторонние репозитории F-Droid", | ||||
|     "steam": "Steam", | ||||
|     "steamMobile": "Steam Mobile", | ||||
|     "steamChat": "Steam Chat", | ||||
| @@ -219,7 +219,7 @@ | ||||
|     "releaseDateAsVersionExplanation": "Этот параметр следует использовать только для приложений, в которых определение версии не работает правильно, но имеется дата выпуска.", | ||||
|     "changes": "Изменения", | ||||
|     "releaseDate": "Дата выпуска", | ||||
|     "importFromURLsInFile": "Импорт из URL-адресов в файле (например, OPML)", | ||||
|     "importFromURLsInFile": "Импорт URL-адресов из файла (например, OPML)", | ||||
|     "versionDetection": "Определение версии", | ||||
|     "standardVersionDetection": "Стандартное определение версии", | ||||
|     "groupByCategory": "Группировать по категориям", | ||||
| @@ -237,8 +237,9 @@ | ||||
|     "removeOnExternalUninstall": "Автоматически убирать из списка удаленные извне приложения", | ||||
|     "pickHighestVersionCode": "Автовыбор кода наивысшей версии APK", | ||||
|     "checkUpdateOnDetailPage": "Проверять наличие обновлений при открытии страницы представления приложения", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "disablePageTransitions": "Отключить анимацию перехода между страницами", | ||||
|     "reversePageTransitions": "Реверс анимации перехода между страницами", | ||||
|     "minStarCount": "Минимальное количество звёзд", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Удалить приложение?", | ||||
|         "other": "Удалить приложения?" | ||||
|   | ||||
| @@ -234,11 +234,12 @@ | ||||
|     "requiresCredentialsInSettings": "此功能需要额外的凭据(在“设置”中添加)", | ||||
|     "checkOnStart": "启动时进行一次检查", | ||||
|     "tryInferAppIdFromCode": "尝试从源代码推断应用 ID", | ||||
|     "removeOnExternalUninstall": "Automatically remove externally uninstalled Apps", | ||||
|     "pickHighestVersionCode": "Auto-select highest version code APK", | ||||
|     "checkUpdateOnDetailPage": "Check for updates on opening an App detail page", | ||||
|     "disablePageTransitions": "Disable page transition animations", | ||||
|     "reversePageTransitions": "Reverse page transition animations", | ||||
|     "removeOnExternalUninstall": "自动删除已卸载的外部应用", | ||||
|     "pickHighestVersionCode": "自动选择版本号最高的 APK 文件", | ||||
|     "checkUpdateOnDetailPage": "打开应用详情页时检查更新", | ||||
|     "disablePageTransitions": "禁用页面过渡动画效果", | ||||
|     "reversePageTransitions": "反转页面过渡动画效果", | ||||
|     "minStarCount": "最小星标数", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "是否删除应用?", | ||||
|         "other": "是否删除应用?" | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
|  | ||||
| class Codeberg extends AppSource { | ||||
|   GitHub gh = GitHub(); | ||||
|   Codeberg() { | ||||
|     host = 'codeberg.org'; | ||||
|  | ||||
| @@ -32,10 +33,9 @@ class Codeberg extends AppSource { | ||||
|     ]; | ||||
|  | ||||
|     canSearch = true; | ||||
|     searchQuerySettingFormItems = gh.searchQuerySettingFormItems; | ||||
|   } | ||||
|  | ||||
|   var gh = GitHub(); | ||||
|  | ||||
|   @override | ||||
|   String sourceSpecificStandardizeURL(String url) { | ||||
|     RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+'); | ||||
| @@ -68,10 +68,12 @@ class Codeberg extends AppSource { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Future<Map<String, List<String>>> search(String query) async { | ||||
|   Future<Map<String, List<String>>> search(String query, | ||||
|       {Map<String, dynamic> querySettings = const {}}) async { | ||||
|     return gh.searchCommon( | ||||
|         query, | ||||
|         'https://$host/api/v1/repos/search?q=${Uri.encodeQueryComponent(query)}&limit=100', | ||||
|         'data'); | ||||
|         'data', | ||||
|         querySettings: querySettings); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -72,7 +72,8 @@ class FDroid extends AppSource { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Future<Map<String, List<String>>> search(String query) async { | ||||
|   Future<Map<String, List<String>>> search(String query, | ||||
|       {Map<String, dynamic> querySettings = const {}}) async { | ||||
|     Response res = await sourceRequest( | ||||
|         'https://search.$host/?q=${Uri.encodeQueryComponent(query)}'); | ||||
|     if (res.statusCode == 200) { | ||||
|   | ||||
| @@ -79,6 +79,21 @@ class GitHub extends AppSource { | ||||
|     ]; | ||||
|  | ||||
|     canSearch = true; | ||||
|     searchQuerySettingFormItems = [ | ||||
|       GeneratedFormTextField('minStarCount', | ||||
|           label: tr('minStarCount'), | ||||
|           defaultValue: '0', | ||||
|           additionalValidators: [ | ||||
|             (value) { | ||||
|               try { | ||||
|                 int.parse(value ?? '0'); | ||||
|               } catch (e) { | ||||
|                 return tr('invalidInput'); | ||||
|               } | ||||
|               return null; | ||||
|             } | ||||
|           ]) | ||||
|     ]; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
| @@ -211,8 +226,8 @@ class GitHub extends AppSource { | ||||
|                 (nameA as String).substring(matchA!.start, matchA.end), | ||||
|                 (nameB as String).substring(matchB!.start, matchB.end)); | ||||
|           } else { | ||||
|             return getReleaseDateFromRelease(a)! | ||||
|                 .compareTo(getReleaseDateFromRelease(b)!); | ||||
|             return (getReleaseDateFromRelease(a) ?? DateTime(1)) | ||||
|                 .compareTo(getReleaseDateFromRelease(b) ?? DateTime(0)); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
| @@ -310,20 +325,26 @@ class GitHub extends AppSource { | ||||
|  | ||||
|   Future<Map<String, List<String>>> searchCommon( | ||||
|       String query, String requestUrl, String rootProp, | ||||
|       {Function(Response)? onHttpErrorCode}) async { | ||||
|       {Function(Response)? onHttpErrorCode, | ||||
|       Map<String, dynamic> querySettings = const {}}) async { | ||||
|     Response res = await sourceRequest(requestUrl); | ||||
|     if (res.statusCode == 200) { | ||||
|       int minStarCount = querySettings['minStarCount'] != null | ||||
|           ? int.parse(querySettings['minStarCount']) | ||||
|           : 0; | ||||
|       Map<String, List<String>> urlsWithDescriptions = {}; | ||||
|       for (var e in (jsonDecode(res.body)[rootProp] as List<dynamic>)) { | ||||
|         urlsWithDescriptions.addAll({ | ||||
|           e['html_url'] as String: [ | ||||
|             e['full_name'] as String, | ||||
|             ((e['archived'] == true ? '[ARCHIVED] ' : '') + | ||||
|                 (e['description'] != null | ||||
|                     ? e['description'] as String | ||||
|                     : tr('noDescription'))) | ||||
|           ] | ||||
|         }); | ||||
|         if ((e['stargazers_count'] ?? e['stars_count'] ?? 0) >= minStarCount) { | ||||
|           urlsWithDescriptions.addAll({ | ||||
|             e['html_url'] as String: [ | ||||
|               e['full_name'] as String, | ||||
|               ((e['archived'] == true ? '[ARCHIVED] ' : '') + | ||||
|                   (e['description'] != null | ||||
|                       ? e['description'] as String | ||||
|                       : tr('noDescription'))) | ||||
|             ] | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|       return urlsWithDescriptions; | ||||
|     } else { | ||||
| @@ -335,13 +356,14 @@ class GitHub extends AppSource { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Future<Map<String, List<String>>> search(String query) async { | ||||
|   Future<Map<String, List<String>>> search(String query, | ||||
|       {Map<String, dynamic> querySettings = const {}}) async { | ||||
|     return searchCommon( | ||||
|         query, | ||||
|         '${await getAPIHost()}/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100', | ||||
|         'items', onHttpErrorCode: (Response res) { | ||||
|       rateLimitErrorCheck(res); | ||||
|     }); | ||||
|     }, querySettings: querySettings); | ||||
|   } | ||||
|  | ||||
|   rateLimitErrorCheck(Response res) { | ||||
|   | ||||
| @@ -69,7 +69,8 @@ class GitLab extends AppSource { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Future<Map<String, List<String>>> search(String query) async { | ||||
|   Future<Map<String, List<String>>> search(String query, | ||||
|       {Map<String, dynamic> querySettings = const {}}) async { | ||||
|     String? PAT = await getPATIfAny(); | ||||
|     if (PAT == null) { | ||||
|       throw CredsNeededError(name); | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import 'dart:math'; | ||||
|  | ||||
| import 'package:hsluv/hsluv.dart'; | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:obtainium/components/generated_form_modal.dart'; | ||||
| @@ -132,19 +133,19 @@ class GeneratedForm extends StatefulWidget { | ||||
|   State<GeneratedForm> createState() => _GeneratedFormState(); | ||||
| } | ||||
|  | ||||
| // Generates a random light color | ||||
| // Courtesy of ChatGPT 😭 (with a bugfix 🥳) | ||||
| // Generates a color in the HSLuv (Pastel) color space | ||||
| // https://pub.dev/documentation/hsluv/latest/hsluv/Hsluv/hpluvToRgb.html | ||||
| Color generateRandomLightColor() { | ||||
|   // Create a random number generator | ||||
|   final Random random = Random(); | ||||
|  | ||||
|   // Generate random hue, saturation, and value values | ||||
|   final double hue = random.nextDouble() * 360; | ||||
|   final double saturation = 0.5 + random.nextDouble() * 0.5; | ||||
|   final double value = 0.9 + random.nextDouble() * 0.1; | ||||
|  | ||||
|   // Create a HSV color with the random values | ||||
|   return HSVColor.fromAHSV(1.0, hue, saturation, value).toColor(); | ||||
|   final randomSeed = Random().nextInt(120); | ||||
|   // https://en.wikipedia.org/wiki/Golden_angle | ||||
|   final goldenAngle = 180 * (3 - sqrt(5)); | ||||
|   // Generate next golden angle hue | ||||
|   final double hue = randomSeed * goldenAngle; | ||||
|   // Map from HPLuv color space to RGB, use constant saturation=100, lightness=70 | ||||
|   final List<double> rgbValuesDbl = Hsluv.hpluvToRgb([hue, 100, 70]); | ||||
|   // Map RBG values from 0-1 to 0-255: | ||||
|   final List<int> rgbValues = rgbValuesDbl.map((rgb) => (rgb * 255).toInt()).toList(); | ||||
|   return Color.fromARGB(255, rgbValues[0], rgbValues[1], rgbValues[2]); | ||||
| } | ||||
|  | ||||
| class _GeneratedFormState extends State<GeneratedForm> { | ||||
| @@ -368,6 +369,36 @@ class _GeneratedFormState extends State<GeneratedForm> { | ||||
|                           )); | ||||
|                     }) ?? | ||||
|                     [const SizedBox.shrink()], | ||||
|                 (values[widget.items[r][e].key] | ||||
|                 as Map<String, MapEntry<int, bool>>?) | ||||
|                     ?.values | ||||
|                     .where((e) => e.value) | ||||
|                     .length == 1 | ||||
|                     ? Padding( | ||||
|                     padding: const EdgeInsets.symmetric(horizontal: 4), | ||||
|                     child: IconButton( | ||||
|                       onPressed: () { | ||||
|                         setState(() { | ||||
|                           var temp = values[widget.items[r][e].key] | ||||
|                               as Map<String, MapEntry<int, bool>>; | ||||
|                           // get selected category str where bool is true | ||||
|                           final oldEntry = temp.entries.firstWhere((entry) => entry.value.value); | ||||
|                           // generate new color, ensure it is not the same | ||||
|                           int newColor = oldEntry.value.key; | ||||
|                           while(oldEntry.value.key == newColor) { | ||||
|                             newColor = generateRandomLightColor().value; | ||||
|                           } | ||||
|                           // Update entry with new color, remain selected | ||||
|                           temp.update(oldEntry.key, (old) => MapEntry(newColor, old.value)); | ||||
|                           values[widget.items[r][e].key] = temp; | ||||
|                           someValueChanged(); | ||||
|                         }); | ||||
|                       }, | ||||
|                       icon: const Icon(Icons.format_color_fill_rounded), | ||||
|                       visualDensity: VisualDensity.compact, | ||||
|                       tooltip: tr('colour'), | ||||
|                     )) | ||||
|                     : const SizedBox.shrink(), | ||||
|                 (values[widget.items[r][e].key] | ||||
|                                 as Map<String, MapEntry<int, bool>>?) | ||||
|                             ?.values | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import 'package:android_package_installer/android_package_installer.dart'; | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:obtainium/providers/logs_provider.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| @@ -101,7 +102,14 @@ showError(dynamic e, BuildContext context) { | ||||
|             title: Text(e is MultiAppMultiError | ||||
|                 ? tr('someErrors') | ||||
|                 : tr('unexpectedError')), | ||||
|             content: Text(e.toString()), | ||||
|             content: GestureDetector( | ||||
|                 onLongPress: () { | ||||
|                   Clipboard.setData(ClipboardData(text: e.toString())); | ||||
|                   ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||||
|                     content: Text(tr('copiedToClipboard')), | ||||
|                   )); | ||||
|                 }, | ||||
|                 child: Text(e.toString())), | ||||
|             actions: [ | ||||
|               TextButton( | ||||
|                   onPressed: () { | ||||
|   | ||||
| @@ -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.18'; | ||||
| const String currentVersion = '0.13.21'; | ||||
| const String currentReleaseTag = | ||||
|     'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES | ||||
|  | ||||
| @@ -39,6 +39,7 @@ List<MapEntry<Locale, String>> supportedLocales = const [ | ||||
|   MapEntry(Locale('es'), 'Español'), | ||||
|   MapEntry(Locale('pl'), 'Polski'), | ||||
|   MapEntry(Locale('ru'), 'Русский язык'), | ||||
|   MapEntry(Locale('bs'), 'Bosanski'), | ||||
| ]; | ||||
| const fallbackLocale = Locale('en'); | ||||
| const localeDir = 'assets/translations'; | ||||
|   | ||||
| @@ -182,7 +182,8 @@ class _ImportExportPageState extends State<ImportExportPage> { | ||||
|                   [ | ||||
|                     GeneratedFormTextField('searchQuery', | ||||
|                         label: tr('searchQuery')) | ||||
|                   ] | ||||
|                   ], | ||||
|                   ...source.searchQuerySettingFormItems.map((e) => [e]) | ||||
|                 ], | ||||
|               ); | ||||
|             }); | ||||
| @@ -191,8 +192,8 @@ class _ImportExportPageState extends State<ImportExportPage> { | ||||
|           setState(() { | ||||
|             importInProgress = true; | ||||
|           }); | ||||
|           var urlsWithDescriptions = | ||||
|               await source.search(values['searchQuery'] as String); | ||||
|           var urlsWithDescriptions = await source | ||||
|               .search(values['searchQuery'] as String, querySettings: values); | ||||
|           if (urlsWithDescriptions.isNotEmpty) { | ||||
|             var selectedUrls = | ||||
|                 // ignore: use_build_context_synchronously | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| import 'dart:math'; | ||||
|  | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:obtainium/components/custom_app_bar.dart'; | ||||
| @@ -21,21 +19,6 @@ class SettingsPage extends StatefulWidget { | ||||
|   State<SettingsPage> createState() => _SettingsPageState(); | ||||
| } | ||||
|  | ||||
| // Generates a random light color | ||||
| // Courtesy of ChatGPT 😭 (with a bugfix 🥳) | ||||
| Color generateRandomLightColor() { | ||||
|   // Create a random number generator | ||||
|   final Random random = Random(); | ||||
|  | ||||
|   // Generate random hue, saturation, and value values | ||||
|   final double hue = random.nextDouble() * 360; | ||||
|   final double saturation = 0.5 + random.nextDouble() * 0.5; | ||||
|   final double value = 0.9 + random.nextDouble() * 0.1; | ||||
|  | ||||
|   // Create a HSV color with the random values | ||||
|   return HSVColor.fromAHSV(1.0, hue, saturation, value).toColor(); | ||||
| } | ||||
|  | ||||
| class _SettingsPageState extends State<SettingsPage> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|   | ||||
| @@ -144,6 +144,24 @@ class AppsProvider with ChangeNotifier { | ||||
|     }(); | ||||
|   } | ||||
|  | ||||
|   Future<File> downloadFileWithRetry( | ||||
|       String url, String fileNameNoExt, Function? onProgress, | ||||
|       {bool useExisting = true, | ||||
|       Map<String, String>? headers, | ||||
|       int retries = 3}) async { | ||||
|     try { | ||||
|       return await downloadFile(url, fileNameNoExt, onProgress, | ||||
|           useExisting: useExisting, headers: headers); | ||||
|     } catch (e) { | ||||
|       if (retries > 0 && e is ClientException) { | ||||
|         return await downloadFileWithRetry(url, fileNameNoExt, onProgress, | ||||
|             useExisting: useExisting, headers: headers, retries: (retries - 1)); | ||||
|       } else { | ||||
|         rethrow; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<File> downloadFile( | ||||
|       String url, String fileNameNoExt, Function? onProgress, | ||||
|       {bool useExisting = true, Map<String, String>? headers}) async { | ||||
| @@ -236,8 +254,9 @@ class AppsProvider with ChangeNotifier { | ||||
|       notificationsProvider?.cancel(notif.id); | ||||
|       int? prevProg; | ||||
|       var fileNameNoExt = '${app.id}-${downloadUrl.hashCode}'; | ||||
|       var downloadedFile = await downloadFile(downloadUrl, fileNameNoExt, | ||||
|           headers: source.requestHeaders, (double? progress) { | ||||
|       var downloadedFile = await downloadFileWithRetry( | ||||
|           downloadUrl, fileNameNoExt, headers: source.requestHeaders, | ||||
|           (double? progress) { | ||||
|         int? prog = progress?.ceil(); | ||||
|         if (apps[app.id] != null) { | ||||
|           apps[app.id]!.downloadProgress = progress; | ||||
| @@ -346,7 +365,7 @@ class AppsProvider with ChangeNotifier { | ||||
|     // If 0 APKs installed, throw the first install error encountered | ||||
|     try { | ||||
|       var somethingInstalled = false; | ||||
|       var firstError = null; | ||||
|       Object? firstError; | ||||
|       for (var file in dir.extracted | ||||
|           .listSync(recursive: true, followLinks: false) | ||||
|           .whereType<File>()) { | ||||
| @@ -366,7 +385,7 @@ class AppsProvider with ChangeNotifier { | ||||
|       } | ||||
|       if (somethingInstalled) { | ||||
|         dir.file.delete(recursive: true); | ||||
|       } else if (firstError) { | ||||
|       } else if (firstError != null) { | ||||
|         throw firstError; | ||||
|       } | ||||
|     } finally { | ||||
|   | ||||
| @@ -436,7 +436,9 @@ abstract class AppSource { | ||||
|   } | ||||
|  | ||||
|   bool canSearch = false; | ||||
|   Future<Map<String, List<String>>> search(String query) { | ||||
|   List<GeneratedFormItem> searchQuerySettingFormItems = []; | ||||
|   Future<Map<String, List<String>>> search(String query, | ||||
|       {Map<String, dynamic> querySettings = const {}}) { | ||||
|     throw NotImplementedError(); | ||||
|   } | ||||
|  | ||||
| @@ -447,8 +449,11 @@ abstract class AppSource { | ||||
| } | ||||
|  | ||||
| ObtainiumError getObtainiumHttpError(Response res) { | ||||
|   return ObtainiumError(res.reasonPhrase ?? | ||||
|       tr('errorWithHttpStatusCode', args: [res.statusCode.toString()])); | ||||
|   return ObtainiumError((res.reasonPhrase != null && | ||||
|           res.reasonPhrase != null && | ||||
|           res.reasonPhrase!.isNotEmpty) | ||||
|       ? res.reasonPhrase! | ||||
|       : tr('errorWithHttpStatusCode', args: [res.statusCode.toString()])); | ||||
| } | ||||
|  | ||||
| abstract class MassAppUrlSource { | ||||
|   | ||||
							
								
								
									
										36
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -222,10 +222,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: file_picker | ||||
|       sha256: b1729fc96627dd44012d0a901558177418818d6bd428df59dcfeb594e5f66432 | ||||
|       sha256: "21145c9c268d54b1f771d8380c195d2d6f655e0567dc1ca2f9c134c02c819e0a" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "5.3.2" | ||||
|     version: "5.3.3" | ||||
|   flutter: | ||||
|     dependency: "direct main" | ||||
|     description: flutter | ||||
| @@ -243,10 +243,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_fgbg | ||||
|       sha256: d37511eef6afb7e2e3f2278ec6498bb12c650ed517c81bcd09452d910e8b9174 | ||||
|       sha256: "08c4d2fd229e3df26083d5aecc3dea9ff4f2d188f8cd57aaf2b3f047bd08a047" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.2.2" | ||||
|     version: "0.3.0" | ||||
|   flutter_launcher_icons: | ||||
|     dependency: "direct dev" | ||||
|     description: | ||||
| @@ -326,6 +326,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "8.2.2" | ||||
|   hsluv: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: hsluv | ||||
|       sha256: f33e63b0c24ceee0f6492874424aa8edc671ef9a20cc889e4b969284d8f02eb1 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.3" | ||||
|   html: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -530,10 +538,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: permission_handler_android | ||||
|       sha256: c0c9754479a4c4b1c1f3862ddc11930c9b3f03bef2816bb4ea6eed1e13551d6f | ||||
|       sha256: "2ffaf52a21f64ac9b35fe7369bb9533edbd4f698e5604db8645b1064ff4cf221" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "10.3.2" | ||||
|     version: "10.3.3" | ||||
|   permission_handler_apple: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -578,10 +586,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: plugin_platform_interface | ||||
|       sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" | ||||
|       sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.4" | ||||
|     version: "2.1.5" | ||||
|   pointycastle: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -775,10 +783,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: url_launcher_android | ||||
|       sha256: "15f5acbf0dce90146a0f5a2c4a002b1814a6303c4c5c075aa2623b2d16156f03" | ||||
|       sha256: "78cb6dea3e93148615109e58e42c35d1ffbf5ef66c44add673d0ab75f12ff3af" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.0.36" | ||||
|     version: "6.0.37" | ||||
|   url_launcher_ios: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -855,10 +863,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_android | ||||
|       sha256: "27ad6a99c4b2d5e1ffd2b993a10f738b6b4979f139b4d64c34ac511595fcd748" | ||||
|       sha256: "8587d0b4991bd0f223f4b4957101c2c7449f905601571315f4967072498dd3fb" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.9.0" | ||||
|     version: "3.9.1" | ||||
|   webview_flutter_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -871,10 +879,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_wkwebview | ||||
|       sha256: "369fdf6160944a7db660ff15fa048c2bd681b09557907beaef1f95e8557d21dc" | ||||
|       sha256: "3e36a8f564809cb7c257ff4278502b185e2191349df0ddee98837f91805c74b8" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.7.0" | ||||
|     version: "3.7.1" | ||||
|   win32: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -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.18+182 # When changing this, update the tag in main() accordingly | ||||
| version: 0.13.21+185 # When changing this, update the tag in main() accordingly | ||||
|  | ||||
| environment: | ||||
|   sdk: '>=2.18.2 <3.0.0' | ||||
| @@ -37,7 +37,7 @@ dependencies: | ||||
|   # Use with the CupertinoIcons class for iOS style icons. | ||||
|   cupertino_icons: ^1.0.5 | ||||
|   path_provider: ^2.0.11 | ||||
|   flutter_fgbg: ^0.2.0 # Try removing reliance on this | ||||
|   flutter_fgbg: ^0.3.0 # Try removing reliance on this | ||||
|   flutter_local_notifications: ^15.1.0+1 | ||||
|   provider: ^6.0.3 | ||||
|   http: ^1.0.0 | ||||
| @@ -64,7 +64,7 @@ dependencies: | ||||
|   android_intent_plus: ^4.0.0 | ||||
|   flutter_markdown: ^0.6.14 | ||||
|   flutter_archive: ^5.0.0 | ||||
|  | ||||
|   hsluv: ^1.1.3 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user