mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-13 13:26:43 +02:00
Synced
This commit is contained in:
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -10,6 +10,7 @@ assignees: ''
|
||||
**Prerequisites**
|
||||
<!-- Please ensure your request is not part of an existing issue. -->
|
||||
<!-- Please ensure you have checked the Obtainium Wiki. -->
|
||||
<!-- Please ensure your request is an actual bug and not intended behaviour (this is frequently the case for issues involving version strings and the HTML source. -->
|
||||
|
||||
**Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
5
.github/ISSUE_TEMPLATE/feature_request.md
vendored
5
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -14,11 +14,14 @@ assignees: ''
|
||||
**Describe the feature**
|
||||
<!-- A clear and concise description of what you want to happen.
|
||||
|
||||
For new Sources, it's preferable (not required) if you suggest how the following details can be extracted from the Source in a reliable way (like an API or through web scraping):
|
||||
For new Sources, please ensure:
|
||||
1. It is not already possible to use the HTML Source for your purposes.
|
||||
2. It must be possible to extract the following details from the Source in a reliable way:
|
||||
- The App version (or any release-specific identifier - a "pseudo-version") for the latest release
|
||||
- One or more APK URL(s) for the latest release
|
||||
- Above details for previous releases (optional)
|
||||
|
||||
If you're not sure about 1 or 2, open a discussion item instead.
|
||||
Note that the Web scraper cannot deal with JavaScript-enabled content. -->
|
||||
|
||||
**Describe alternatives you've considered (if applicable)**
|
||||
|
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@ -15,6 +15,15 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '17'
|
||||
|
||||
- name: Flutter Doctor
|
||||
id: flutter_doctor
|
||||
run: |
|
||||
flutter doctor -v
|
||||
|
||||
- name: Import GPG key
|
||||
id: import_pgp_key
|
||||
@ -42,7 +51,6 @@ jobs:
|
||||
if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi
|
||||
echo "beta=$BETA" >> $GITHUB_OUTPUT
|
||||
TAG="v$VERSION"
|
||||
if [ $BETA == true ]; then TAG="$TAG"-beta; fi
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build APKs
|
||||
|
@ -18,7 +18,6 @@ Currently supported App sources:
|
||||
- [F-Droid](https://f-droid.org/)
|
||||
- Third Party F-Droid Repos
|
||||
- [IzzyOnDroid](https://android.izzysoft.de/)
|
||||
- [SourceForge](https://sourceforge.net/)
|
||||
- [SourceHut](https://git.sr.ht/)
|
||||
- Other - General:
|
||||
- [APKPure](https://apkpure.net/)
|
||||
@ -42,6 +41,12 @@ Currently supported App sources:
|
||||
[<img src="https://github.com/machiav3lli/oandbackupx/blob/034b226cea5c1b30eb4f6a6f313e4dadcbb0ece4/badge_github.png"
|
||||
alt="Get it on GitHub"
|
||||
height="80">](https://github.com/ImranR98/Obtainium/releases)
|
||||
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png"
|
||||
alt="Get it on IzzyOnDroid"
|
||||
height="80">](https://apt.izzysoft.de/fdroid/index/apk/dev.imranr.obtainium)
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/packages/dev.imranr.obtainium.fdroid/)
|
||||
|
||||
[PGP Public Key](https://keyserver.ubuntu.com/pks/lookup?search=contact%40imranr.dev&fingerprint=on&op=index)
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
@ -21,11 +22,6 @@ if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'dev.rikka.tools.refine'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
@ -33,7 +29,8 @@ if (keystorePropertiesFile.exists()) {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
namespace "dev.imranr.obtainium"
|
||||
compileSdk flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
@ -54,7 +51,7 @@ android {
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||
minSdkVersion 24
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
@ -70,8 +70,6 @@
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
tools:node="remove" />
|
||||
android:maxSdkVersion="29" />\
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
</manifest>
|
@ -1,22 +1,3 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.8.10'
|
||||
ext {
|
||||
compileSdkVersion = 34 // or latest
|
||||
targetSdkVersion = 34 // or latest
|
||||
appCompatVersion = "1.4.2" // or latest
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:7.4.2"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "dev.rikka.tools.refine:gradle-plugin:4.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
|
@ -1,11 +1,25 @@
|
||||
include ':app'
|
||||
pluginManagement {
|
||||
def flutterSdkPath = {
|
||||
def properties = new Properties()
|
||||
file("local.properties").withInputStream { properties.load(it) }
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
return flutterSdkPath
|
||||
}()
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
assert localPropertiesFile.exists()
|
||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.4.2" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.10" apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Uklonite zastarjeli filter aplikacija",
|
||||
"showOutdatedOnly": "Prikaži samo zastarjele aplikacije",
|
||||
"filter": "Filtriranje",
|
||||
"filterActive": "Filtriranje",
|
||||
"filterApps": "Filtriraj aplikacije",
|
||||
"appName": "Naziv aplikacije",
|
||||
"author": "Autor",
|
||||
@ -219,7 +218,7 @@
|
||||
"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)",
|
||||
"gitlabPATLabel": "GitLab token za lični pristup",
|
||||
"about": "O nama",
|
||||
"requiresCredentialsInSettings": "{}: Za ovo su potrebni dodatni akreditivi (u Postavkama)",
|
||||
"checkOnStart": "Provjerite ima li novosti pri pokretanju",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Dodajte ove informacije ispod.",
|
||||
"addInfoInSettings": "Dodajte ove informacije u Postavkama.",
|
||||
"githubSourceNote": "GitHub ograničavanje se može izbjeći korišćenjem tokena za lični pristup.",
|
||||
"gitlabSourceNote": "GitLab APK preuzimanje možda neće raditi bez tokena za lični pristup.",
|
||||
"sortByLastLinkSegment": "Sortiraj samo po zadnjem segmentu veze",
|
||||
"filterReleaseNotesByRegEx": "Filtirajte promjene u izdanju po regularnom izrazu",
|
||||
"customLinkFilterRegex": "Prilagođeni APK link filtrira se po regularnom izrazu (Zadano '.apk$')",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Note",
|
||||
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||
"badDownload": "The APK could not be parsed (incompatible or partial download)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Share new Apps with AppVerifier (if available)",
|
||||
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Želite li ukloniti aplikaciju?",
|
||||
"other": "Želite li ukloniti aplikacije?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} i još jedna aplikacija je vjerovatno ažurirana.",
|
||||
"other": "{} i još {} aplikacija su vjerovatno ažurirane."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Odstranit filtr Neaktuální",
|
||||
"showOutdatedOnly": "Zobrazovat pouze zastaralé aplikace",
|
||||
"filter": "Filtr",
|
||||
"filterActive": "Filtr *",
|
||||
"filterApps": "Filtrovat aplikace",
|
||||
"appName": "Název aplikace",
|
||||
"author": "Autor",
|
||||
@ -219,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Jen sledované'",
|
||||
"dontShowAPKOriginWarnings": "Nezobrazovat varování pro původ APK",
|
||||
"moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace",
|
||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Umožňuje vyhledávání a lepší zjišťování APK)",
|
||||
"gitlabPATLabel": "Osobní přístupový token GitLab",
|
||||
"about": "O",
|
||||
"requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)",
|
||||
"checkOnStart": "Zkontrolovat jednou při spuštění",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Přidat tuto informaci na konec stránky.",
|
||||
"addInfoInSettings": "Přidat tuto informaci do nastavení.",
|
||||
"githubSourceNote": "Omezení rychlosti GitHub lze obejít pomocí klíče API.",
|
||||
"gitlabSourceNote": "Extrakce GitLab APK nemusí fungovat bez klíče API",
|
||||
"sortByLastLinkSegment": "Seřadit pouze podle poslední části odkazu",
|
||||
"filterReleaseNotesByRegEx": "Filtrovat poznámky k vydání podle regulárního výrazu",
|
||||
"customLinkFilterRegex": "Vlastní filtr odkazů APK podle regulárního výrazu (výchozí '.apk$')",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Poznámka",
|
||||
"selfHostedNote": "Rozbalovací seznam \"{}\" lze použít k dosažení vlastních/obvyklých instancí libovolného zdroje.",
|
||||
"badDownload": "APK nelze analyzovat (nekompatibilní nebo částečné stažení)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Sdílení nových aplikací s aplikací AppVerifier (pokud je k dispozici)",
|
||||
"appVerifierInstructionToast": "Sdílejte do aplikace AppVerifier a po dokončení se sem vraťte.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Odstranit Apku?",
|
||||
"other": "Odstranit Apky?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} a 1 další aplikace možno aktualizovat",
|
||||
"other": "{} a {} další aplikace mohou být aktualizovány."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APK"
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
"app": "App",
|
||||
"appsFromSourceAreTrackOnly": "Apps aus dieser Quelle sind nur zum Nachverfolgen.",
|
||||
"youPickedTrackOnly": "Sie haben die Option „Nur Nachverfolgen“ gewählt.",
|
||||
"trackOnlyAppDescription": "Die App wird auf Updates überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.",
|
||||
"trackOnlyAppDescription": "Die App wird auf Aktualisierungen überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.",
|
||||
"cancelled": "Abgebrochen",
|
||||
"appAlreadyAdded": "App bereits hinzugefügt",
|
||||
"alreadyUpToDateQuestion": "App bereits auf dem neuesten Stand?",
|
||||
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "App-Filter ‚Nicht aktuell‘ entfernen",
|
||||
"showOutdatedOnly": "Nur nicht aktuelle Apps anzeigen",
|
||||
"filter": "Filter",
|
||||
"filterActive": "Filter *",
|
||||
"filterApps": "Apps filtern",
|
||||
"appName": "App Name",
|
||||
"author": "Autor",
|
||||
@ -219,26 +218,25 @@
|
||||
"dontShowTrackOnlyWarnings": "Warnung für 'Nur Nachverfolgen' nicht anzeigen",
|
||||
"dontShowAPKOriginWarnings": "Warnung für APK-Herkunft nicht anzeigen",
|
||||
"moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben",
|
||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Aktiviert Suche und bessere APK Entdeckung)",
|
||||
"gitlabPATLabel": "GitLab Personal Access Token",
|
||||
"about": "Über",
|
||||
"requiresCredentialsInSettings": "{}: Benötigt zusätzliche Anmeldedaten (in den Einstellungen)",
|
||||
"checkOnStart": "Überprüfe einmalig beim Start",
|
||||
"tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln",
|
||||
"removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps",
|
||||
"pickHighestVersionCode": "Automatische Auswahl des APK mit höchstem Versionscode",
|
||||
"checkUpdateOnDetailPage": "Nach Updates suchen, wenn eine App-Detailseite geöffnet wird",
|
||||
"checkUpdateOnDetailPage": "Nach Aktualisierungen suchen, wenn eine App-Detailseite geöffnet wird",
|
||||
"disablePageTransitions": "Animationen für Seitenübergänge deaktivieren",
|
||||
"reversePageTransitions": "Umgekehrte Animationen für Seitenübergänge",
|
||||
"minStarCount": "Minimale Anzahl von Sternen",
|
||||
"addInfoBelow": "Fügen Sie diese Informationen unten hinzu.",
|
||||
"addInfoInSettings": "Fügen Sie diese Info in den Einstellungen hinzu.",
|
||||
"githubSourceNote": "Die GitHub-Ratenbegrenzung kann mit einem API-Schlüssel umgangen werden.",
|
||||
"gitlabSourceNote": "GitLab APK-Extraktion funktioniert möglicherweise nicht ohne API-Schlüssel",
|
||||
"sortByLastLinkSegment": "Sortiere nur nach dem letzten Teil des Links",
|
||||
"filterReleaseNotesByRegEx": "Versionshinweise nach regulärem Ausdruck filtern",
|
||||
"customLinkFilterRegex": "Benutzerdefinierter APK Link Filter nach Regulärem Ausdruck (Standard '.apk$')",
|
||||
"appsPossiblyUpdated": "App Aktualisierungen wurden versucht",
|
||||
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Updates für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden",
|
||||
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Aktualisierungen für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden",
|
||||
"xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.",
|
||||
"enableBackgroundUpdates": "Aktiviere Hintergrundaktualisierungen",
|
||||
"backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.",
|
||||
@ -268,7 +266,7 @@
|
||||
"runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
|
||||
"versionExtractWholePage": "Versions-Extraktion per RegEx auf die gesamte Seite anwenden",
|
||||
"installing": "Installiere",
|
||||
"skipUpdateNotifications": "Keine Benachrichtigung zu App-Updates geben",
|
||||
"skipUpdateNotifications": "Keine Benachrichtigung zu App-Aktualisierungen geben",
|
||||
"updatesAvailableNotifChannel": "Aktualisierungen verfügbar",
|
||||
"appsUpdatedNotifChannel": "Apps aktualisiert",
|
||||
"appsPossiblyUpdatedNotifChannel": "App Aktualisierungen wurden versucht",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Hinweis",
|
||||
"selfHostedNote": "Das „{}“-Dropdown-Menü kann verwendet werden, um selbst gehostete/angepasste Instanzen einer beliebigen Quelle zu erreichen.",
|
||||
"badDownload": "Die APK konnte nicht geparst werden (inkompatibler oder teilweiser Download)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Neue Apps mit AppVerifier teilen (falls verfügbar)",
|
||||
"appVerifierInstructionToast": "Geben Sie die Daten an AppVerifier weiter und kehren Sie dann hierher zurück, wenn Sie fertig sind.",
|
||||
"removeAppQuestion": {
|
||||
"one": "App entfernen?",
|
||||
"other": "Apps entfernen?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
|
||||
"other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "Don't show 'Track-Only' warnings",
|
||||
"dontShowAPKOriginWarnings": "Don't show APK origin warnings",
|
||||
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
||||
"gitlabPATLabel": "GitLab Personal Access Token",
|
||||
"about": "About",
|
||||
"requiresCredentialsInSettings": "{} needs additional credentials (in Settings)",
|
||||
"checkOnStart": "Check for updates on startup",
|
||||
@ -232,7 +232,6 @@
|
||||
"addInfoBelow": "Add this info below.",
|
||||
"addInfoInSettings": "Add this info in the Settings.",
|
||||
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
|
||||
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
|
||||
"sortByLastLinkSegment": "Sort by only the last segment of the link",
|
||||
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
||||
@ -298,6 +297,8 @@
|
||||
"note": "Note",
|
||||
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||
"badDownload": "The APK could not be parsed (incompatible or partial download)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Share new Apps with AppVerifier (if available)",
|
||||
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Remove App?",
|
||||
"other": "Remove Apps?"
|
||||
@ -349,5 +350,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} and 1 more app may have been updated.",
|
||||
"other": "{} and {} more apps may have been updated."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
{
|
||||
"invalidURLForSource": "URL de la aplicación {} no es válida",
|
||||
"invalidURLForSource": "El URL de la aplicación {} no es válido",
|
||||
"noReleaseFound": "No se ha podido encontrar una versión válida",
|
||||
"noVersionFound": "No se ha podido determinar la versión",
|
||||
"urlMatchesNoSource": "La URL no coincide con ninguna fuente conocida",
|
||||
"urlMatchesNoSource": "El URL no coincide con ninguna fuente conocida",
|
||||
"cantInstallOlderVersion": "No se puede instalar una versión previa de la aplicación",
|
||||
"appIdMismatch": "La ID del paquete descargado no coincide con la ID de la aplicación instalada",
|
||||
"appIdMismatch": "El id. del paquete descargado no coincide con la ID de la aplicación instalada",
|
||||
"functionNotImplemented": "Esta clase no ha implementado esta función",
|
||||
"placeholder": "Espacio reservado",
|
||||
"someErrors": "Han ocurrido algunos errores",
|
||||
"unexpectedError": "Error inesperado",
|
||||
"ok": "OK",
|
||||
"ok": "Aceptar",
|
||||
"and": "y",
|
||||
"githubPATLabel": "Token de acceso personal a GitHub\n(reduce tiempos de espera)",
|
||||
"includePrereleases": "Incluir versiones preliminares",
|
||||
@ -34,76 +34,75 @@
|
||||
"cancelled": "Cancelado",
|
||||
"appAlreadyAdded": "Aplicación añadida anteriormente",
|
||||
"alreadyUpToDateQuestion": "¿Aplicación actualizada previamente?",
|
||||
"addApp": "Añadir Aplicación",
|
||||
"addApp": "Añadir aplicación",
|
||||
"appSourceURL": "URL de origen de la aplicación",
|
||||
"error": "Error",
|
||||
"add": "Añadir",
|
||||
"searchSomeSourcesLabel": "Buscar (solo algunas fuentes)",
|
||||
"search": "Buscar",
|
||||
"additionalOptsFor": "Opciones Adicionales para {}",
|
||||
"supportedSources": "Fuentes Soportadas",
|
||||
"additionalOptsFor": "Opciones adicionales para {}",
|
||||
"supportedSources": "Fuentes admitidas",
|
||||
"trackOnlyInBrackets": "(Solo seguimiento)",
|
||||
"searchableInBrackets": "(soporta búsqueda)",
|
||||
"searchableInBrackets": "(permite búsqueda)",
|
||||
"appsString": "Aplicaciones",
|
||||
"noApps": "Sin Aplicaciones",
|
||||
"noAppsForFilter": "Sin aplicaciones para filtrar",
|
||||
"byX": "por: {}",
|
||||
"percentProgress": "Progreso: {}%",
|
||||
"pleaseWait": "Por favor, espere",
|
||||
"updateAvailable": "Actualización Disponible",
|
||||
"notInstalled": "No Instalado",
|
||||
"percentProgress": "Progreso: {} %",
|
||||
"pleaseWait": "Espere un momento",
|
||||
"updateAvailable": "Actualización disponible",
|
||||
"notInstalled": "No instalado",
|
||||
"pseudoVersion": "pseudoversión",
|
||||
"selectAll": "Seleccionar Todo",
|
||||
"selectAll": "Seleccionar todo",
|
||||
"deselectX": "Deseleccionar {}",
|
||||
"xWillBeRemovedButRemainInstalled": "{} será eliminada de Obtainium pero continuará instalada en el dispositivo.",
|
||||
"xWillBeRemovedButRemainInstalled": "{} se eliminará de Obtainium pero continuará instalada en el dispositivo.",
|
||||
"removeSelectedAppsQuestion": "¿Eliminar aplicaciones seleccionadas?",
|
||||
"removeSelectedApps": "Eliminar Aplicaciones Seleccionadas",
|
||||
"removeSelectedApps": "Eliminar aplicaciones seleccionadas",
|
||||
"updateX": "Actualizar {}",
|
||||
"installX": "Instalar {}",
|
||||
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo seguimiento)\ncomo actualizada",
|
||||
"changeX": "Cambiar {}",
|
||||
"installUpdateApps": "Instalar/Actualizar aplicaciones",
|
||||
"installUpdateSelectedApps": "Instalar/Actualizar aplicaciones seleccionadas",
|
||||
"installUpdateApps": "Instalar/actualizar aplicaciones",
|
||||
"installUpdateSelectedApps": "Instalar/actualizar aplicaciones seleccionadas",
|
||||
"markXSelectedAppsAsUpdated": "¿Marcar {} aplicaciones seleccionadas como actualizadas?",
|
||||
"no": "No",
|
||||
"yes": "Sí",
|
||||
"markSelectedAppsUpdated": "Marcar aplicaciones seleccionadas como actualizadas",
|
||||
"pinToTop": "Fijar arriba",
|
||||
"unpinFromTop": "Desfijar de arriba",
|
||||
"pinToTop": "Anclar al principio",
|
||||
"unpinFromTop": "Desanclar del principio",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "¿Restuarar estado de instalación para las aplicaciones seleccionadas?",
|
||||
"installStatusOfXWillBeResetExplanation": "Se restaurará el estado de instalación de las aplicaciones seleccionadas.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
|
||||
"customLinkMessage": "Estos enlaces funcionan en dispositivos con Obtainium instalado",
|
||||
"shareAppConfigLinks": "Compartir la configuración de la aplicación como enlace HTML",
|
||||
"shareSelectedAppURLs": "Compartir URLs de las aplicaciones seleccionadas",
|
||||
"shareSelectedAppURLs": "Compartir URL de las aplicaciones seleccionadas",
|
||||
"resetInstallStatus": "Restaurar estado de instalación",
|
||||
"more": "Más",
|
||||
"removeOutdatedFilter": "Elimiar filtro de aplicaciones desactualizado",
|
||||
"removeOutdatedFilter": "Eliminar filtro de aplicaciones desactualizado",
|
||||
"showOutdatedOnly": "Mostrar solo aplicaciones desactualizadas",
|
||||
"filter": "Filtrar",
|
||||
"filterActive": "Filtrar *",
|
||||
"filterApps": "Filtrar Actualizaciones",
|
||||
"filterApps": "Filtrar actualizaciones",
|
||||
"appName": "Nombre de la aplicación",
|
||||
"author": "Autor",
|
||||
"upToDateApps": "Aplicaciones Actualizadas",
|
||||
"nonInstalledApps": "Aplicaciones No Instaladas",
|
||||
"importExport": "Importar/Exportar",
|
||||
"upToDateApps": "Aplicaciones actualizadas",
|
||||
"nonInstalledApps": "Aplicaciones no instaladas",
|
||||
"importExport": "Importar/exportar",
|
||||
"settings": "Ajustes",
|
||||
"exportedTo": "Exportado a {}",
|
||||
"obtainiumExport": "Exportar Obtainium",
|
||||
"invalidInput": "Input incorrecto",
|
||||
"invalidInput": "Entrada no válida",
|
||||
"importedX": "Importado {}",
|
||||
"obtainiumImport": "Importar Obtainium",
|
||||
"importFromURLList": "Importar desde lista de URLs",
|
||||
"searchQuery": "Consulta de Búsqueda",
|
||||
"appURLList": "Lista de URLs de Aplicaciones",
|
||||
"importFromURLList": "Importar desde lista de URL",
|
||||
"searchQuery": "Término de búsqueda",
|
||||
"appURLList": "Lista de URL de aplicaciones",
|
||||
"line": "Línea",
|
||||
"searchX": "Buscar {}",
|
||||
"noResults": "Resultados no encontrados",
|
||||
"noResults": "No se encontró ningún resultado",
|
||||
"importX": "Importar desde {}",
|
||||
"importedAppsIdDisclaimer": "Las aplicaciones importadas podrían mostrarse incorrectamente como \"No Instalada\".\nPara solucionarlo, reinstálalas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a las URLs y a los métodos de importación mediante terceros.",
|
||||
"importedAppsIdDisclaimer": "Las aplicaciones importadas podrían mostrarse incorrectamente como «No instalada».\nPara solucionarlo, reinstálelas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a los URL y a los métodos de importación mediante terceros.",
|
||||
"importErrors": "Errores de Importación",
|
||||
"importedXOfYApps": "{} de {} Aplicaciones importadas.",
|
||||
"followingURLsHadErrors": "Las siguientes URLs han tenido problemas:",
|
||||
"importedXOfYApps": "{} de {} aplicaciones importadas.",
|
||||
"followingURLsHadErrors": "Los URL siguientes han tenido problemas:",
|
||||
"selectURL": "Seleccionar URL",
|
||||
"selectURLs": "Seleccionar URLs",
|
||||
"pick": "Escoger",
|
||||
@ -111,9 +110,9 @@
|
||||
"dark": "Oscuro",
|
||||
"light": "Claro",
|
||||
"followSystem": "Seguir al sistema",
|
||||
"useBlackTheme": "Negro puro en tema Oscuro",
|
||||
"appSortBy": "Ordenar Apps Por",
|
||||
"authorName": "Autor/Nombre",
|
||||
"useBlackTheme": "Negro puro en tema oscuro",
|
||||
"appSortBy": "Ordenar aplicaciones por",
|
||||
"authorName": "Autor/nombre",
|
||||
"nameAuthor": "Nombre/Autor",
|
||||
"asAdded": "Según se Añadieron",
|
||||
"appSortOrder": "Orden de Clasificación",
|
||||
@ -123,22 +122,22 @@
|
||||
"neverManualOnly": "Nunca, solo manual",
|
||||
"appearance": "Apariencia",
|
||||
"showWebInAppView": "Mostrar vista de la web de origen",
|
||||
"pinUpdates": "Fijar actualizaciones al principio",
|
||||
"pinUpdates": "Anclar actualizaciones al principio",
|
||||
"updates": "Actualizaciones",
|
||||
"sourceSpecific": "Fuente Específica",
|
||||
"sourceSpecific": "Fuente específica",
|
||||
"appSource": "Obtainium en GitHub",
|
||||
"noLogs": "Sin Logs",
|
||||
"appLogs": "Logs",
|
||||
"noLogs": "Ningún registro",
|
||||
"appLogs": "Registros",
|
||||
"close": "Cerrar",
|
||||
"share": "Compartir",
|
||||
"appNotFound": "Aplicación no encontrada",
|
||||
"obtainiumExportHyphenatedLowercase": "obtainium-exportación",
|
||||
"pickAnAPK": "Seleccione una APK",
|
||||
"appHasMoreThanOnePackage": "{} tiene más de un paquete:",
|
||||
"deviceSupportsXArch": "Su dispositivo soporta las siguientes arquitecturas de procesador: {}.",
|
||||
"deviceSupportsFollowingArchs": "Su dispositivo soporta las siguientes arquitecturas de procesador:",
|
||||
"deviceSupportsXArch": "Su dispositivo admite las siguientes arquitecturas de procesador: {}.",
|
||||
"deviceSupportsFollowingArchs": "Su dispositivo admite las siguientes arquitecturas de procesador:",
|
||||
"warning": "Aviso",
|
||||
"sourceIsXButPackageFromYPrompt": "La fuente de la aplicación es '{}' pero el paquete de la actualización viene de '{}'. ¿Desea continuar?",
|
||||
"sourceIsXButPackageFromYPrompt": "La fuente de la aplicación es «{}» pero el paquete de la actualización viene de «{}». ¿Desea continuar?",
|
||||
"updatesAvailable": "Actualizaciones Disponibles",
|
||||
"updatesAvailableNotifDescription": "Notifica al usuario de que hay actualizaciones para una o más aplicaciones monitoreadas por Obtainium",
|
||||
"noNewUpdates": "No hay nuevas actualizaciones.",
|
||||
@ -146,17 +145,17 @@
|
||||
"appsUpdated": "Aplicaciones actualizadas",
|
||||
"appsUpdatedNotifDescription": "Notifica al usuario de que una o más aplicaciones han sido actualizadas en segundo plano",
|
||||
"xWasUpdatedToY": "{} ha sido actualizada a {}.",
|
||||
"errorCheckingUpdates": "Error buscando ectualizaciones",
|
||||
"errorCheckingUpdates": "Error al buscar actualizaciones",
|
||||
"errorCheckingUpdatesNotifDescription": "Una notificación que muestra cuándo la comprobación de actualizaciones en segundo plano falla",
|
||||
"appsRemoved": "Aplicaciones eliminadas",
|
||||
"appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas",
|
||||
"xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}",
|
||||
"completeAppInstallation": "Instalación completa de la aplicación",
|
||||
"obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierto para instalar aplicaciones",
|
||||
"completeAppInstallationNotifDescription": "Pide al usuario volver a Obtainium para terminar de instalar una aplicación",
|
||||
"completeAppInstallationNotifDescription": "Le pide volver a Obtainium para terminar de instalar una aplicación",
|
||||
"checkingForUpdates": "Buscando actualizaciones...",
|
||||
"checkingForUpdatesNotifDescription": "Notificación temporal que aparece al buscar actualizaciones",
|
||||
"pleaseAllowInstallPerm": "Por favor, permita que Obtainium instale aplicaciones",
|
||||
"pleaseAllowInstallPerm": "Permita que Obtainium instale aplicaciones",
|
||||
"trackOnly": "Solo para seguimiento",
|
||||
"errorWithHttpStatusCode": "Error {}",
|
||||
"versionCorrectionDisabled": "Corrección de versiones desactivada (el plugin parece no funcionar)",
|
||||
@ -174,8 +173,8 @@
|
||||
"appWithIdOrNameNotFound": "No se han encontrado aplicaciones con esa ID o nombre",
|
||||
"reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones",
|
||||
"fdroidThirdPartyRepo": "Repositorio de tercera parte F-Droid",
|
||||
"steamMobile": "Móvil de vapor",
|
||||
"steamChat": "Chat de vapor",
|
||||
"steamMobile": "Steam para móviles",
|
||||
"steamChat": "Chat de Steam",
|
||||
"install": "Instalar",
|
||||
"markInstalled": "Marcar como instalada",
|
||||
"update": "Actualizar",
|
||||
@ -193,14 +192,14 @@
|
||||
"noCategory": "Sin categoría",
|
||||
"noCategories": "Sin categorías",
|
||||
"deleteCategoriesQuestion": "¿Eliminar categorías?",
|
||||
"categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas serán marcadas como 'Sin categoría'.",
|
||||
"categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas se marcarán como «Sin categoría».",
|
||||
"addCategory": "Añadir categoría",
|
||||
"label": "Nombre",
|
||||
"language": "Idioma",
|
||||
"copiedToClipboard": "Copiado al portapapeles",
|
||||
"copiedToClipboard": "Se copió en el portapapeles",
|
||||
"storagePermissionDenied": "Permiso de almacenamiento rechazado",
|
||||
"selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaciones seleccionadas.",
|
||||
"filterAPKsByRegEx": "Filtrar por APKs",
|
||||
"filterAPKsByRegEx": "Filtrar por APK",
|
||||
"removeFromObtainium": "Eliminar de Obtainium",
|
||||
"uninstallFromDevice": "Desinstalar del dispositivo",
|
||||
"onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.",
|
||||
@ -213,13 +212,13 @@
|
||||
"versionDetection": "Detección de versiones",
|
||||
"standardVersionDetection": "Por versión",
|
||||
"groupByCategory": "Agrupar por categoría",
|
||||
"autoApkFilterByArch": "Filtrar APKs por arquitectura del procesador (si es posible)",
|
||||
"overrideSource": "Sobrescribir Fuente",
|
||||
"autoApkFilterByArch": "Filtrar APK por arquitectura del procesador (si es posible)",
|
||||
"overrideSource": "Anular fuente",
|
||||
"dontShowAgain": "No mostrar de nuevo",
|
||||
"dontShowTrackOnlyWarnings": "No mostrar avisos sobre apps en 'solo seguimiento'",
|
||||
"dontShowAPKOriginWarnings": "No mostrar avisos sobre las fuentes de las APKs",
|
||||
"moveNonInstalledAppsToBottom": "Mover Apps no instaladas al final",
|
||||
"gitlabPATLabel": "Token de acceso personal a GitLab\n(habilita la búsqueda y mejor detección de APKs)",
|
||||
"gitlabPATLabel": "Token de acceso personal a GitLab",
|
||||
"about": "Acerca",
|
||||
"requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en ajustes)",
|
||||
"checkOnStart": "Comprobar actualizaciones al inicio",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Añadir esta información debajo.",
|
||||
"addInfoInSettings": "Puede añadir esta información en Ajustes.",
|
||||
"githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con un 'token de acceso personal'.",
|
||||
"gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin un 'token de acceso personal'.",
|
||||
"sortByLastLinkSegment": "Ordenar sólo por el último segmento del enlace",
|
||||
"filterReleaseNotesByRegEx": "Filtrar por notas de versión (release notes)",
|
||||
"customLinkFilterRegex": "Filtro personalizado de enlace APK (por defecto '.apk$')",
|
||||
@ -264,7 +262,7 @@
|
||||
"takeFirstLink": "Usar primer enlace",
|
||||
"skipSort": "Omitir orden",
|
||||
"debugMenu": "Menu Depurar",
|
||||
"bgTaskStarted": "Iniciada tarea en segundo plano - revisa los logs.",
|
||||
"bgTaskStarted": "Iniciada tarea en segundo plano; revise los registros.",
|
||||
"runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano",
|
||||
"versionExtractWholePage": "Aplicar la versión de extracción regex a la página entera",
|
||||
"installing": "Instalando",
|
||||
@ -299,23 +297,25 @@
|
||||
"latest": "Versión más reciente",
|
||||
"invertRegEx": "Invertir expresión regular",
|
||||
"note": "Nota",
|
||||
"selfHostedNote": "El desplegable \"{}\" puede utilizarse para acceder a instancias autoalojadas/personalizadas de cualquier fuente.",
|
||||
"selfHostedNote": "El desplegable «{}» puede utilizarse para acceder a instancias autoalojadas/personalizadas de cualquier fuente.",
|
||||
"badDownload": "No se ha podido analizar el APK (incompatible o descarga parcial)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Compartir nuevas aplicaciones con AppVerifier (si está disponible)",
|
||||
"appVerifierInstructionToast": "Comparta con AppVerifier y vuelva aquí cuando esté listo.",
|
||||
"removeAppQuestion": {
|
||||
"one": "¿Eliminar Aplicación?",
|
||||
"other": "¿Eliminar Aplicaciones?"
|
||||
"one": "¿Eliminar aplicación?",
|
||||
"other": "¿Eliminar aplicaciones?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Muchas peticiones (limitado) - prueba de nuevo en {} minuto",
|
||||
"other": "Muchas peticiones (limitado) - prueba de nuevo en {} minutos"
|
||||
"one": "Muchas peticiones (limitado); pruebe de nuevo en {} minuto",
|
||||
"other": "Muchas peticiones (limitado); pruebe de nuevo en {} minutos"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "La comprobación de actualizaciones en segundo plano se ha encontrado un {}, se volverá a probar en {} minuto",
|
||||
"other": "La comprobación de actualizaciones en segundo plano se ha encontrado un {}, se volverá a probar en {} minutos"
|
||||
"one": "La comprobación de actualizaciones en segundo plano se ha encontrado un {}; se volverá a probar en {} minuto",
|
||||
"other": "La comprobación de actualizaciones en segundo plano se ha encontrado un {}; se volverá a probar en {} minutos"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualización - se notificará al usuario si es necesario",
|
||||
"other": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualizaciones - se notificará al usuario si es necesario"
|
||||
"one": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualización; se le notificará si es necesario",
|
||||
"other": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualizaciones; se le notificará si es necesario"
|
||||
},
|
||||
"apps": {
|
||||
"one": "{} Aplicación",
|
||||
@ -338,19 +338,23 @@
|
||||
"other": "{} días"
|
||||
},
|
||||
"clearedNLogsBeforeXAfterY": {
|
||||
"one": "Eliminado {n} log (previo a = {before}, posterior a = {after})",
|
||||
"other": "Eliminados {n} logs (previos a = {before}, posteriores a = {after})"
|
||||
"one": "Eliminado {n} registro (previo a = {before}, posterior a = {after})",
|
||||
"other": "Eliminados {n} registros (previos a = {before}, posteriores a = {after})"
|
||||
},
|
||||
"xAndNMoreUpdatesAvailable": {
|
||||
"one": "{} y 1 aplicación más tiene actualizaciones.",
|
||||
"other": "{} y {} aplicaciones más tienen actualizaciones."
|
||||
},
|
||||
"xAndNMoreUpdatesInstalled": {
|
||||
"one": "{} y 1 aplicación más han sido actualizadas.",
|
||||
"other": "{} y {} aplicaciones más han sido actualizadas."
|
||||
"one": "{} y 1 aplicación más se han actualizado.",
|
||||
"other": "{} y {} aplicaciones más se han actualizado."
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} y 1 aplicación más podría haber sido actualizada.",
|
||||
"other": "{} y {} aplicaciones más podrían haber sido actualizadas."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "فیلتر برنامه قدیمی را حذف کنید",
|
||||
"showOutdatedOnly": "فقط برنامه های قدیمی را نشان دهید",
|
||||
"filter": "فیلتر",
|
||||
"filterActive": "فیلتر *",
|
||||
"filterApps": "فیلتر کردن برنامه ها",
|
||||
"appName": "نام برنامه",
|
||||
"author": "سازنده",
|
||||
@ -219,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "هشدار 'فقط ردیابی' را نشان ندهید",
|
||||
"dontShowAPKOriginWarnings": "هشدارهای منبع APK را نشان ندهید",
|
||||
"moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید",
|
||||
"gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو و کشف بهتر APK را فعال میکند)",
|
||||
"gitlabPATLabel": "رمز دسترسی شخصی GitLab",
|
||||
"about": "درباره",
|
||||
"requiresCredentialsInSettings": "{}: این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)",
|
||||
"checkOnStart": "بررسی در شروع",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "این اطلاعات را در زیر اضافه کنید",
|
||||
"addInfoInSettings": "این اطلاعات را در تنظیمات اضافه کنید.",
|
||||
"githubSourceNote": "با استفاده از کلید API می توان از محدودیت نرخ GitHub جلوگیری کرد.",
|
||||
"gitlabSourceNote": "استخراج APK GitLab ممکن است بدون کلید API کار نکند.",
|
||||
"sortByLastLinkSegment": "فقط بر اساس آخرین بخش پیوند مرتب کنید",
|
||||
"filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید",
|
||||
"customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیشفرض '.apk$')",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Note",
|
||||
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||
"badDownload": "The APK could not be parsed (incompatible or partial download)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Share new Apps with AppVerifier (if available)",
|
||||
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
|
||||
"removeAppQuestion": {
|
||||
"one": "برنامه حذف شود؟",
|
||||
"other": "برنامه ها حذف شوند؟"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.",
|
||||
"other": "ممکن است {} و {} برنامه های دیگر به روز شده باشند."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Supprimer le filtre d'application obsolète",
|
||||
"showOutdatedOnly": "Afficher uniquement les applications obsolètes",
|
||||
"filter": "Filtre",
|
||||
"filterActive": "Filtre *",
|
||||
"filterApps": "Filtrer les applications",
|
||||
"appName": "Nom de l'application",
|
||||
"author": "Auteur",
|
||||
@ -168,7 +167,7 @@
|
||||
"lastUpdateCheckX": "Vérification de la dernière mise à jour : {}",
|
||||
"remove": "Retirer",
|
||||
"yesMarkUpdated": "Oui, marquer comme mis à jour",
|
||||
"fdroid": "F-Droïde Officiel",
|
||||
"fdroid": "F-Droid Officiel",
|
||||
"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",
|
||||
@ -219,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "Don't Show the 'Track-Only' Warning",
|
||||
"dontShowAPKOriginWarnings": "Ne pas afficher les avertissements sur l'origine de l'APK",
|
||||
"moveNonInstalledAppsToBottom": "Déplacer les applications non installées vers le bas de la vue Applications",
|
||||
"gitlabPATLabel": "Jeton d'accès personnel GitLab\\n (permet la recherche et une meilleure découverte d'APK)",
|
||||
"gitlabPATLabel": "Jeton d'accès personnel GitLab",
|
||||
"about": "À propos de",
|
||||
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
|
||||
"checkOnStart": "Vérifier les mises à jour au démarrage",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Ajoutez ces informations ci-dessous.",
|
||||
"addInfoInSettings": "Ajoutez ces informations dans les paramètres.",
|
||||
"githubSourceNote": "La limitation du débit GitHub peut être évitée à l'aide d'une clé API.",
|
||||
"gitlabSourceNote": "L'extraction d'APK GitLab peut ne pas fonctionner sans clé API.",
|
||||
"sortByLastLinkSegment": "Trier uniquement sur le dernier segment du lien",
|
||||
"filterReleaseNotesByRegEx": "Filtrer les notes de version par expression régulière",
|
||||
"customLinkFilterRegex": "Filtre de lien APK personnalisé par expression régulière (par défaut '.apk$')",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Note",
|
||||
"selfHostedNote": "La liste déroulante \"{}\" peut être utilisée pour accéder aux instances auto-hébergées/personnalisées de n'importe quelle source.",
|
||||
"badDownload": "L'APK n'a pas pu être analysé (téléchargement incompatible ou partiel)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Partager les nouvelles applications avec AppVerifier (si disponible)",
|
||||
"appVerifierInstructionToast": "Partagez avec AppVerifier, puis revenez ici lorsque vous êtes prêt.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Supprimer l'application ?",
|
||||
"other": "Supprimer les applications ?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"une": "{} et 1 application supplémentaire ont peut-être été mises à jour.",
|
||||
"other": "{} et {} autres applications peuvent avoir été mises à jour."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Távolítsa el az elavult app szűrőt",
|
||||
"showOutdatedOnly": "Csak az elavult appok megjelenítése",
|
||||
"filter": "Szűrő",
|
||||
"filterActive": "Szűrő *",
|
||||
"filterApps": "Appok szűrése",
|
||||
"appName": "App név",
|
||||
"author": "Szerző",
|
||||
@ -219,7 +218,7 @@
|
||||
"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": "Helyezze át a nem telepített appokat az App nézet aljára",
|
||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Engedélyezi a Keresést és jobb APK felfedezés)",
|
||||
"gitlabPATLabel": "GitLab személyes hozzáférési token",
|
||||
"about": "Rólunk",
|
||||
"requiresCredentialsInSettings": "{}: Ehhez további hitelesítő adatokra van szükség (a Beállításokban)",
|
||||
"checkOnStart": "Egyszer az alkalmazás indításakor is",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Adja hozzá ezt az infót alább.",
|
||||
"addInfoInSettings": "Adja hozzá ezt az infót a Beállításokban.",
|
||||
"githubSourceNote": "A GitHub sebességkorlátozás elkerülhető API-kulcs használatával.",
|
||||
"gitlabSourceNote": "Előfordulhat, hogy a GitLab APK kibontása nem működik API-kulcs nélkül.",
|
||||
"sortByLastLinkSegment": "Rendezés csak a link utolsó szegmense szerint",
|
||||
"filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel",
|
||||
"customLinkFilterRegex": "Egyéni APK hivatkozásszűrő reguláris kifejezéssel (Alapérték '.apk$')",
|
||||
@ -283,14 +281,14 @@
|
||||
"parallelDownloads": "Párhuzamos letöltéseket enged",
|
||||
"installMethod": "Telepítési mód",
|
||||
"normal": "Normál",
|
||||
"root": "Gyökér",
|
||||
"root": "Root",
|
||||
"shizukuBinderNotFound": "A Shizuku nem fut",
|
||||
"useSystemFont": "Használja a rendszer betűtípusát",
|
||||
"systemFontError": "Hiba a rendszer betűtípusának betöltésekor: {}",
|
||||
"useVersionCodeAsOSVersion": "Az app verziókód használata a rendszer által észlelt verzióként",
|
||||
"requestHeader": "Kérelem fejléc",
|
||||
"useLatestAssetDateAsReleaseDate": "Használja a legújabb tartalomfeltöltést megjelenési dátumként",
|
||||
"defaultPseudoVersioningMethod": "Alapértelmezett álversziós módszer",
|
||||
"defaultPseudoVersioningMethod": "Alapértelmezett álverziós módszer",
|
||||
"partialAPKHash": "Részleges APK Hash",
|
||||
"APKLinkHash": "APK Link Hash",
|
||||
"directAPKLink": "Közvetlen APK Link",
|
||||
@ -301,9 +299,11 @@
|
||||
"note": "Megjegyzés:",
|
||||
"selfHostedNote": "A \"{}\" legördülő menü használható bármely forrás saját üzemeltetésű/egyéni példányainak eléréséhez.",
|
||||
"badDownload": "Az APK-t nem lehetett elemezni (inkompatibilis vagy részleges letöltés)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Új alkalmazások megosztása az AppVerifierrel (ha elérhető)",
|
||||
"appVerifierInstructionToast": "Ossza meg az AppVerifierrel, majd térjen vissza ide, ha kész.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Eltávolítja az alkalmazást?",
|
||||
"other": "Eltávolítja az alkalmazást?"
|
||||
"other": "Eltávolítja az alkalmazásokat?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva",
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} és 1 további alkalmazás is frissült.",
|
||||
"other": "{} és {} további alkalmazás is frissült."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APK-k"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"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",
|
||||
"author": "Autore",
|
||||
@ -219,7 +218,7 @@
|
||||
"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\n(attiva la ricerca e migliora la rilevazione di apk)",
|
||||
"gitlabPATLabel": "GitLab Token di accesso personale",
|
||||
"about": "Informazioni",
|
||||
"requiresCredentialsInSettings": "{}: Servono credenziali aggiuntive (in Impostazioni)",
|
||||
"checkOnStart": "Controlla una volta all'avvio",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Aggiungi questa info sotto.",
|
||||
"addInfoInSettings": "Aggiungi questa info nelle impostazioni.",
|
||||
"githubSourceNote": "Il limite di ricerca GitHub può essere evitato usando una chiave API.",
|
||||
"gitlabSourceNote": "L'estrazione di APK da GitLab potrebbe non funzionare senza chiave API.",
|
||||
"sortByLastLinkSegment": "Ordina solo in base all'ultimo segmento del collegamento",
|
||||
"filterReleaseNotesByRegEx": "Filtra le note di rilascio con espressione regolare",
|
||||
"customLinkFilterRegex": "Filtra link APK personalizzato con espressione regolare (predefinito '.apk$')",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Nota",
|
||||
"selfHostedNote": "Il menu a tendina \"{}\" può essere usato per raggiungere istanze autogestite/personali di qualsiasi fonte.",
|
||||
"badDownload": "Non è stato possibile analizzare l'APK (download incompatibile o parziale).",
|
||||
"beforeNewInstallsShareToAppVerifier": "Condividere le nuove applicazioni con AppVerifier (se disponibile)",
|
||||
"appVerifierInstructionToast": "Condividete con AppVerifier, quindi tornate qui quando siete pronti.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Rimuovere l'app?",
|
||||
"other": "Rimuovere le app?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} e un'altra app potrebbero essere state aggiornate.",
|
||||
"other": "{} e altre {} app potrebbero essere state aggiornate."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APK"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "アップデートが存在するアプリのフィルターを解除",
|
||||
"showOutdatedOnly": "アップデートが存在するアプリのみ表示する",
|
||||
"filter": "フィルター",
|
||||
"filterActive": "フィルター *",
|
||||
"filterApps": "アプリをフィルタリングする",
|
||||
"appName": "アプリ名",
|
||||
"author": "作者",
|
||||
@ -219,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "「追跡のみ」の警告を表示しない",
|
||||
"dontShowAPKOriginWarnings": "APKのダウンロード元の警告を表示しない",
|
||||
"moveNonInstalledAppsToBottom": "未インストールのアプリをアプリ一覧の下部に移動させる",
|
||||
"gitlabPATLabel": "GitLab パーソナルアクセストークン\n(検索とより良いAPK検出の有効化)",
|
||||
"gitlabPATLabel": "GitLab パーソナルアクセストークン",
|
||||
"about": "概要",
|
||||
"requiresCredentialsInSettings": "{}: これには追加の認証が必要です (設定にて)",
|
||||
"checkOnStart": "起動時にアップデートを確認する",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "下部でこの情報を追加してください。",
|
||||
"addInfoInSettings": "設定でこの情報を追加してください。",
|
||||
"githubSourceNote": "GitHubのレート制限はAPIキーを使うことで回避できます。",
|
||||
"gitlabSourceNote": "GitLabのAPK抽出はAPIキーがないと動作しない場合があります。",
|
||||
"sortByLastLinkSegment": "リンクの最後のセグメントのみでソートする",
|
||||
"filterReleaseNotesByRegEx": "正規表現でリリースノートをフィルタリングする",
|
||||
"customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "注",
|
||||
"selfHostedNote": "ドロップダウン\"{}\"を使用すると、あらゆるソースのセルフホスト/カスタムインスタンスにアクセスできます。",
|
||||
"badDownload": "APK を解析できませんでした(互換性がないか、部分的にダウンロードされています)。",
|
||||
"beforeNewInstallsShareToAppVerifier": "AppVerifierで新しいアプリを共有する(利用可能な場合)",
|
||||
"appVerifierInstructionToast": "AppVerifierに共有し、準備ができたらここに戻ってください。",
|
||||
"removeAppQuestion": {
|
||||
"one": "アプリを削除しますか?",
|
||||
"other": "アプリを削除しますか?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} とさらに 1 個のアプリがアップデートされた可能性があります。",
|
||||
"other": "{} とさらに {} 個のアプリがアップデートされた可能性があります。"
|
||||
},
|
||||
"apk": {
|
||||
"one": "{}APK",
|
||||
"other": "{}APK"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Verwijder out-of-date app filter",
|
||||
"showOutdatedOnly": "Toon alleen out-of-date apps",
|
||||
"filter": "Filter",
|
||||
"filterActive": "Filteren *",
|
||||
"filterApps": "Filter apps",
|
||||
"appName": "App naam",
|
||||
"author": "Auteur",
|
||||
@ -219,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "Geen waarschuwingen voor 'Track-Only' weergeven",
|
||||
"dontShowAPKOriginWarnings": "APK-herkomstwaarschuwingen niet weergeven",
|
||||
"moveNonInstalledAppsToBottom": "Verplaats niet-geïnstalleerde apps naar de onderkant van de apps-weergave",
|
||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Maakt het mogelijk beter te zoeken naar APK's)",
|
||||
"gitlabPATLabel": "GitLab persoonlijk toegangskenmerk",
|
||||
"about": "Over",
|
||||
"requiresCredentialsInSettings": "{}: Dit vereist aanvullende referenties (in Instellingen)",
|
||||
"checkOnStart": "Controleren op updates bij opstarten",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Voeg deze informatie hieronder toe.",
|
||||
"addInfoInSettings": "Voeg deze informatie toe in de instellingen.",
|
||||
"githubSourceNote": "Beperkingen van GitHub kunnen worden vermeden door het gebruik van een API-sleutel.",
|
||||
"gitlabSourceNote": "GitLab APK-extractie werkt mogelijk niet zonder een API-sleutel.",
|
||||
"sortByLastLinkSegment": "Sorteren op alleen het laatste segment van de link",
|
||||
"filterReleaseNotesByRegEx": "Filter release-opmerkingen met een reguliere expressie.",
|
||||
"customLinkFilterRegex": "Aangepaste APK-linkfilter met een reguliere expressie (Standaard '.apk$').",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Opmerking",
|
||||
"selfHostedNote": "De \"{}\" dropdown kan gebruikt worden om zelf gehoste/aangepaste instanties van elke bron te bereiken.",
|
||||
"badDownload": "De APK kon niet worden verwerkt (incompatibele of gedeeltelijke download)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Nieuwe Apps delen met AppVerifier (indien beschikbaar)",
|
||||
"appVerifierInstructionToast": "Deel naar AppVerifier en keer hier terug als je klaar bent.",
|
||||
"removeAppQuestion": {
|
||||
"one": "App verwijderen?",
|
||||
"other": "Apps verwijderen?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} en nog 1 app zijn mogelijk bijgewerkt.",
|
||||
"other": "{} en {} meer apps zijn mogelijk bijgwerkt."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APK's"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Usuń filtr nieaktualnych aplikacji",
|
||||
"showOutdatedOnly": "Pokaż tylko nieaktualne aplikacje",
|
||||
"filter": "FIltr",
|
||||
"filterActive": "Filtruj *",
|
||||
"filterApps": "Filtruj aplikacje",
|
||||
"appName": "Nazwa aplikacji",
|
||||
"author": "Autor",
|
||||
@ -219,7 +218,7 @@
|
||||
"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)",
|
||||
"gitlabPATLabel": "Osobisty token dostępu GitLab",
|
||||
"about": "Więcej informacji",
|
||||
"requiresCredentialsInSettings": "{}: Wymaga to dodatkowych poświadczeń (w Ustawieniach)",
|
||||
"checkOnStart": "Sprawdź aktualizacje przy uruchomieniu",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Dodaj tę informację poniżej.",
|
||||
"addInfoInSettings": "Dodaj tę informację w Ustawieniach.",
|
||||
"githubSourceNote": "Limit żądań GitHub można ominąć za pomocą klucza API.",
|
||||
"gitlabSourceNote": "Pozyskiwanie pliku APK z GitLab może nie działać bez klucza API.",
|
||||
"sortByLastLinkSegment": "Sortuj tylko według ostatniego segmentu łącza",
|
||||
"filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
|
||||
"customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Uwaga",
|
||||
"selfHostedNote": "Lista rozwijana \"{}\" może być używana do uzyskiwania dostępu do samodzielnie hostowanych / niestandardowych instancji dowolnego źródła.",
|
||||
"badDownload": "Nie można przeanalizować pliku APK (niekompatybilny lub częściowo pobrany).",
|
||||
"beforeNewInstallsShareToAppVerifier": "Udostępnianie nowych aplikacji za pomocą AppVerifier (jeśli dostępne)",
|
||||
"appVerifierInstructionToast": "Udostępnij w AppVerifier, a następnie wróć tutaj, gdy będziesz gotowy.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Usunąć aplikację?",
|
||||
"few": "Usunąć aplikacje?",
|
||||
@ -378,5 +378,9 @@
|
||||
"few": "{} i {} inne apki mogły zostać zaktualizowane.",
|
||||
"many": "{} i {} innych apek mogło zostać zaktualizowanych.",
|
||||
"other": "{} i {} inne apki mogły zostać zaktualizowane."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APK"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Remover filtro de aplicativos desatualizados",
|
||||
"showOutdatedOnly": "Mostrar apenas aplicativos desatualizados",
|
||||
"filter": "Filtro",
|
||||
"filterActive": "Filtro *",
|
||||
"filterApps": "Filtrar aplicativos",
|
||||
"appName": "Nome do aplicativo",
|
||||
"author": "Autor",
|
||||
@ -219,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas monitorar'",
|
||||
"dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK",
|
||||
"moveNonInstalledAppsToBottom": "Mover aplicativos não instalados para o fundo da lista de aplicativos",
|
||||
"gitlabPATLabel": "Token de acesso pessoal do Gitlab\n(Ativa pesquisa e melhora a descoberta de APKs)",
|
||||
"gitlabPATLabel": "Token de acesso pessoal do Gitlab",
|
||||
"about": "Sobre",
|
||||
"requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)",
|
||||
"checkOnStart": "Verificar se há atualizações ao iniciar",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Adicionar essa informação abaixo.",
|
||||
"addInfoInSettings": "Adicionar essa informação nas configurações.",
|
||||
"githubSourceNote": "A limitação de taxa do GitHub pode ser evitada usando uma chave de API.",
|
||||
"gitlabSourceNote": "A extração de endereço de download do APK no GitLab provavelmente não funcione sem que seja fornecido uma chave de API.",
|
||||
"sortByLastLinkSegment": "Ordenar apenas usando o último segmento do link",
|
||||
"filterReleaseNotesByRegEx": "Filtrar notas de versão usando Regex",
|
||||
"customLinkFilterRegex": "Filtro de link personalizado usando expressão regular (Padrão '.apk$')",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Nota",
|
||||
"selfHostedNote": "O menu suspenso \"{}\" pode ser usado para acessar instâncias auto-hospedadas/personalizadas de qualquer fonte.",
|
||||
"badDownload": "Não foi possível analisar o APK (transferência incompatível ou parcial)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Partilhar novas aplicações com o AppVerifier (se disponível)",
|
||||
"appVerifierInstructionToast": "Partilhe com o AppVerifier e, em seguida, regresse aqui quando estiver pronto.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Remover aplicativo?",
|
||||
"other": "Remover aplicativos?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} e um outro aplicativo podem ter sido atualizados.",
|
||||
"other": "{} e {} outros aplicativos podem ter sido atualizados."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Удалить фильтр для устаревших приложений",
|
||||
"showOutdatedOnly": "Показывать только устаревшие приложения",
|
||||
"filter": "Фильтр",
|
||||
"filterActive": "Фильтр *",
|
||||
"filterApps": "Фильтровать приложения",
|
||||
"appName": "Название приложения",
|
||||
"author": "Автор",
|
||||
@ -219,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "Не показывать предупреждения о только отслеживаемых приложениях",
|
||||
"dontShowAPKOriginWarnings": "Не показывать предупреждения об отличающемся источнике APK-файлов",
|
||||
"moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка",
|
||||
"gitlabPATLabel": "Персональный токен доступа GitLab\n(включает поиск и улучшает обнаружение APK)",
|
||||
"gitlabPATLabel": "Персональный токен доступа GitLab",
|
||||
"about": "Описание",
|
||||
"requiresCredentialsInSettings": "{}: Для этого требуются дополнительные учетные данные (в настройках)",
|
||||
"checkOnStart": "Проверять наличие обновлений при запуске",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Добавьте эту информацию ниже",
|
||||
"addInfoInSettings": "Добавьте эту информацию в Настройки",
|
||||
"githubSourceNote": "Используя ключ API можно обойти лимит запросов GitHub",
|
||||
"gitlabSourceNote": "Без ключа API может не работать извлечение APK с GitLab",
|
||||
"sortByLastLinkSegment": "Сортировать только по последнему сегменту ссылки",
|
||||
"filterReleaseNotesByRegEx": "Фильтровать примечания к выпуску\n(регулярное выражение)",
|
||||
"customLinkFilterRegex": "Пользовательский фильтр ссылок APK\n(регулярное выражение, по умолчанию: '.apk$')",
|
||||
@ -299,6 +297,8 @@
|
||||
"note": "Примечание",
|
||||
"selfHostedNote": "Выпадающий список \"{}\" можно использовать для доступа к самостоятельно размещенным/настроенным экземплярам любого источника.",
|
||||
"badDownload": "APK не удалось разобрать (несовместимая или неполная загрузка)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Поделитесь новыми приложениями с AppVerifier (если доступно)",
|
||||
"appVerifierInstructionToast": "Поделитесь с AppVerifier, а затем вернитесь сюда, когда будете готовы.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Удалить приложение?",
|
||||
"other": "Удалить приложения?"
|
||||
@ -350,5 +350,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} и ещё 1 приложение могли быть обновлены",
|
||||
"other": "{} и ещё {} приложений могли быть обновлены"
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
||||
|
@ -58,11 +58,10 @@ const main = async () => {
|
||||
return `${translationsDir}/${f}`
|
||||
}).filter(f => f.endsWith('.json') && f != templateFile)
|
||||
|
||||
const templateTranslation = require(templateFile)
|
||||
|
||||
const templateTranslation = JSON.parse(fs.readFileSync(templateFile).toString())
|
||||
|
||||
otherFiles.forEach(file => {
|
||||
const thisTranslationOriginal = require(file)
|
||||
const thisTranslationOriginal = JSON.parse(fs.readFileSync((file).toString()))
|
||||
const thisTranslationNew = {}
|
||||
Object.keys(templateTranslation).forEach(k => {
|
||||
thisTranslationNew[k] = thisTranslationOriginal[k] || templateTranslation[k]
|
||||
@ -72,7 +71,7 @@ const main = async () => {
|
||||
|
||||
for (let i in otherFiles) {
|
||||
const file = otherFiles[i]
|
||||
const thisTranslation = require(file)
|
||||
const thisTranslation = JSON.parse(fs.readFileSync((file).toString()))
|
||||
const translationKeys = Object.keys(templateTranslation)
|
||||
for (let j in translationKeys) {
|
||||
const k = translationKeys[j]
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Ta bort Utgånga App-filtret",
|
||||
"showOutdatedOnly": "Visa Endast Utgånga Appar",
|
||||
"filter": "Filtrera",
|
||||
"filterActive": "Filter *",
|
||||
"filterApps": "Filtrera Appar",
|
||||
"appName": "Appnamn",
|
||||
"author": "Utvecklare",
|
||||
@ -219,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "Visa inte 'Följ-Endast' varningar",
|
||||
"dontShowAPKOriginWarnings": "Visa inte APK-ursprung varningar",
|
||||
"moveNonInstalledAppsToBottom": "Flytta icke-installerade appar till botten av appvyn",
|
||||
"gitlabPATLabel": "GitLab Personal Access Token\\n(Möjliggör sökning och bättre APK-upptäckt)",
|
||||
"gitlabPATLabel": "Personligt åtkomsttoken för GitLab",
|
||||
"about": "Om",
|
||||
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
|
||||
"checkOnStart": "Kolla efter uppdateringar vid start",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Lägg till denna information nedanför.",
|
||||
"addInfoInSettings": "Lägg till denna information i Inställningar.",
|
||||
"githubSourceNote": "GitHub-hastighetsbegränsning kan undvikas med en API-nyckel.",
|
||||
"gitlabSourceNote": "GitLab APK-extraktion kanske inte fungerar utan en API-nyckel.",
|
||||
"sortByLastLinkSegment": "Sortera endast efter det sista segmentet av länken",
|
||||
"filterReleaseNotesByRegEx": "Filtrera versionskommentarer efter reguljärt uttryck",
|
||||
"customLinkFilterRegex": "Anpassad APK-länkfiltrera efter reguljärt uttryck (standard '.apk$')",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Anmärkning",
|
||||
"selfHostedNote": "Rullgardinsmenyn \"{}\" kan användas för att nå självhostade/anpassade instanser av valfri källa.",
|
||||
"badDownload": "APK kunde inte analyseras (inkompatibel eller partiell nedladdning)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Dela nya appar med AppVerifier (om tillgängligt)",
|
||||
"appVerifierInstructionToast": "Dela till AppVerifier och återvänd sedan hit när du är klar.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Ta Bort App?",
|
||||
"other": "Ta Bort Appar?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} och 1 till app kan ha uppdaterats.",
|
||||
"other": "{} och {} appar till kan ha uppdaterats."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APK:er"
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "Güncel Olmayan Uygulama Filtresini Kaldır",
|
||||
"showOutdatedOnly": "Yalnızca Güncel Olmayan Uygulamaları Göster",
|
||||
"filter": "Filtre",
|
||||
"filterActive": "Filtre *",
|
||||
"filterApps": "Uygulamaları Filtrele",
|
||||
"appName": "Uygulama Adı",
|
||||
"author": "Yazar",
|
||||
@ -219,7 +218,7 @@
|
||||
"dontShowTrackOnlyWarnings": "'Yalnızca Takip Edilen' uyarılarını gösterme",
|
||||
"dontShowAPKOriginWarnings": "APK kaynağı uyarılarını gösterme",
|
||||
"moveNonInstalledAppsToBottom": "Yüklenmemiş Uygulamaları Uygulamalar Görünümünün Altına Taşı",
|
||||
"gitlabPATLabel": "GitLab Kişisel Erişim Belirteci\n(Arama ve Daha İyi APK Keşfi İçin)",
|
||||
"gitlabPATLabel": "GitLab Kişisel Erişim Belirteci",
|
||||
"about": "Hakkında",
|
||||
"requiresCredentialsInSettings": "{}: Bu, ek kimlik bilgilerine ihtiyaç duyar (Ayarlar'da)",
|
||||
"checkOnStart": "Başlangıçta güncellemeleri kontrol et",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "Bu bilgiyi aşağıya ekle.",
|
||||
"addInfoInSettings": "Bu bilgiyi Ayarlar'da ekleyin.",
|
||||
"githubSourceNote": "GitHub hız sınırlaması bir API anahtarı kullanılarak atlanabilir.",
|
||||
"gitlabSourceNote": "GitLab APK çıkarma işlemi bir API anahtarı olmadan çalışmayabilir.",
|
||||
"sortByLastLinkSegment": "Bağlantının yalnızca son bölümüne göre sırala",
|
||||
"filterReleaseNotesByRegEx": "Sürüm Notlarını Düzenli İfade ile Filtrele",
|
||||
"customLinkFilterRegex": "Özel APK Bağlantı Filtresi Düzenli İfade ile (Varsayılan '.apk$')",
|
||||
@ -301,6 +299,8 @@
|
||||
"note": "Not",
|
||||
"selfHostedNote": "\"{}\" açılır menüsü, herhangi bir kaynağın kendi kendine barındırılan/özel örneklerine ulaşmak için kullanılabilir.",
|
||||
"badDownload": "APK ayrıştırılamadı (uyumsuz veya kısmi indirme)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Yeni Uygulamaları AppVerifier ile paylaşın (varsa)",
|
||||
"appVerifierInstructionToast": "AppVerifier ile paylaşın, hazır olduğunuzda buraya dönün.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Uygulamayı Kaldır?",
|
||||
"other": "Uygulamaları Kaldır?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} ve 1 diğer uygulama muhtemelen güncellendi.",
|
||||
"other": "{} ve {} daha fazla uygulama muhtemelen güncellendi."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APK'lar"
|
||||
}
|
||||
}
|
||||
|
360
assets/translations/uk.json
Normal file
360
assets/translations/uk.json
Normal file
@ -0,0 +1,360 @@
|
||||
{
|
||||
"invalidURLForSource": "Неправильна URL-адреса для джерела застосунку {}",
|
||||
"noReleaseFound": "Не вдалося знайти відповідне видання",
|
||||
"noVersionFound": "Не вдалося визначити версію видання",
|
||||
"urlMatchesNoSource": "URL не відповідає відомому джерелу",
|
||||
"cantInstallOlderVersion": "Не можна встановити старішу версію застосунку",
|
||||
"appIdMismatch": "Ідентифікатор пакета, завантажений, не відповідає ідентифікатору існуючого застосунку",
|
||||
"functionNotImplemented": "Цей клас не реалізував цю функцію",
|
||||
"placeholder": "Заповнювач",
|
||||
"someErrors": "Виникла деяка помилка",
|
||||
"unexpectedError": "Неочікувана помилка",
|
||||
"ok": "Добре",
|
||||
"and": "та",
|
||||
"githubPATLabel": "Персональний ключ доступу GitHub (збільшує обмеження на швидкість)",
|
||||
"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": "Додаткові опції для {}",
|
||||
"supportedSources": "Підтримувані джерела",
|
||||
"trackOnlyInBrackets": "(Тільки для відстеження)",
|
||||
"searchableInBrackets": "(Можливий пошук)",
|
||||
"appsString": "Застосунки",
|
||||
"noApps": "Застосунків немає",
|
||||
"noAppsForFilter": "Застосунків для фільтрації немає",
|
||||
"byX": "Від {}",
|
||||
"percentProgress": "Прогрес: {}%",
|
||||
"pleaseWait": "Будь ласка, зачекайте",
|
||||
"updateAvailable": "Доступно оновлення",
|
||||
"notInstalled": "Не встановлено",
|
||||
"pseudoVersion": "псевдо-версія",
|
||||
"selectAll": "Вибрати все",
|
||||
"deselectX": "Скасувати вибір {}",
|
||||
"xWillBeRemovedButRemainInstalled": "{} буде видалено з Obtainium, але залишиться встановленим на пристрої.",
|
||||
"removeSelectedAppsQuestion": "Видалити вибрані застосунки?",
|
||||
"removeSelectedApps": "Видалити вибрані застосунки",
|
||||
"updateX": "Оновити {}",
|
||||
"installX": "Встановити {}",
|
||||
"markXTrackOnlyAsUpdated": "Позначити {}\n(Тільки відстежування)\nяк оновлено",
|
||||
"changeX": "Змінити {}",
|
||||
"installUpdateApps": "Встановити/Оновити застосунки",
|
||||
"installUpdateSelectedApps": "Встановити/Оновити вибрані застосунки",
|
||||
"markXSelectedAppsAsUpdated": "Позначити {} вибрані застосунки як оновлені?",
|
||||
"no": "Ні",
|
||||
"yes": "Так",
|
||||
"markSelectedAppsUpdated": "Позначити вибрані застосунки як оновлені",
|
||||
"pinToTop": "Закріпити угорі",
|
||||
"unpinFromTop": "Відкріпити зверху",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Скинути статус встановлення для вибраних застосунків?",
|
||||
"installStatusOfXWillBeResetExplanation": "Статус встановлення будь-яких вибраних застосунків буде скинутий.\n\nЦе може допомогти, коли версія застосунку, відображена в Obtainium, є неправильною через невдалі оновлення або інші проблеми.",
|
||||
"customLinkMessage": "Ці посилання працюють на пристроях з встановленим Obtainium",
|
||||
"shareAppConfigLinks": "Поділитися посиланнями на конфігурацію Застосунку як HTML",
|
||||
"shareSelectedAppURLs": "Поділитися вибраними URL-адресами застосунків",
|
||||
"resetInstallStatus": "Скинути статус встановлення",
|
||||
"more": "Більше",
|
||||
"removeOutdatedFilter": "Видалити фільтр застарілих застосунків",
|
||||
"showOutdatedOnly": "Показати лише застарілі застосунки",
|
||||
"filter": "Фільтр",
|
||||
"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-адресах:",
|
||||
"selectURL": "Вибрати URL",
|
||||
"selectURLs": "Вибрати URL-адреси",
|
||||
"pick": "Вибрати",
|
||||
"theme": "Тема",
|
||||
"dark": "Темна",
|
||||
"light": "Світла",
|
||||
"followSystem": "Дотримуватися системи",
|
||||
"useBlackTheme": "Використовувати чорну тему (Amoled)",
|
||||
"appSortBy": "Сортувати застосунки за",
|
||||
"authorName": "Автор/Назва",
|
||||
"nameAuthor": "Назва/Автор",
|
||||
"asAdded": "За додаванням",
|
||||
"appSortOrder": "Порядок сортування застосунків",
|
||||
"ascending": "За зростанням",
|
||||
"descending": "За спаданням",
|
||||
"bgUpdateCheckInterval": "Інтервал перевірки оновлень у фоновому режимі",
|
||||
"neverManualOnly": "Ніколи - Тільки вручну",
|
||||
"appearance": "Вигляд",
|
||||
"showWebInAppView": "Показати джерело застосунку у вигляді веб-сторінки",
|
||||
"pinUpdates": "Закріпити оновлення у верхній частині вигляду застосунків",
|
||||
"updates": "Оновлення",
|
||||
"sourceSpecific": "Певне джерело",
|
||||
"appSource": "Джерело застосунку",
|
||||
"noLogs": "Немає логів",
|
||||
"appLogs": "Лог застосунку",
|
||||
"close": "Закрити",
|
||||
"share": "Поділитися",
|
||||
"appNotFound": "Застосунок не знайдено",
|
||||
"obtainiumExportHyphenatedLowercase": "експорт з Obtainium",
|
||||
"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": "Помилка {} HTTP-коду",
|
||||
"versionCorrectionDisabled": "Виправлення версії вимкнено (здається, плагін не працює)",
|
||||
"unknown": "Невідомо",
|
||||
"none": "Нічого",
|
||||
"never": "Ніколи",
|
||||
"latestVersionX": "Остання версія: {}",
|
||||
"installedVersionX": "Встановлено: {}",
|
||||
"lastUpdateCheckX": "Остання перевірка оновлень: {}",
|
||||
"remove": "Видалити",
|
||||
"yesMarkUpdated": "Так, позначити як оновлене",
|
||||
"fdroid": "F-Droid Офіційний",
|
||||
"appIdOrName": "Ідентифікатор або назва застосунку",
|
||||
"appId": "Ідентифікатор застосунку",
|
||||
"appWithIdOrNameNotFound": "Застосунок з таким ідентифікатором або назвою не знайдено",
|
||||
"reposHaveMultipleApps": "Сховища можуть містити кілька застосунків",
|
||||
"fdroidThirdPartyRepo": "F-Droid Стороннє сховище",
|
||||
"steamMobile": "Мобільний Steam",
|
||||
"steamChat": "Чат Steam",
|
||||
"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)",
|
||||
"versionDetectionExplanation": "Порівняти рядок версії з версією, визначеною операційною системою",
|
||||
"versionDetection": "Визначення версії",
|
||||
"standardVersionDetection": "Стандартне визначення версії",
|
||||
"groupByCategory": "Групувати за категоріями",
|
||||
"autoApkFilterByArch": "Спробувати фільтрувати APK за архітектурою ЦП, якщо можливо",
|
||||
"overrideSource": "Перевизначити джерело",
|
||||
"dontShowAgain": "Не показувати це знову",
|
||||
"dontShowTrackOnlyWarnings": "Не показувати попередження про 'Тільки відстеження'",
|
||||
"dontShowAPKOriginWarnings": "Не показувати попередження про походження APK",
|
||||
"moveNonInstalledAppsToBottom": "Перемістити невстановлені застосунки вниз у перегляді застосунків",
|
||||
"gitlabPATLabel": "Особистий токен GitLab (Увімкнення пошуку та краще виявлення APK)",
|
||||
"about": "Про програму",
|
||||
"requiresCredentialsInSettings": "{} потребує додаткових облікових даних (у налаштуваннях)",
|
||||
"checkOnStart": "Перевірити наявність оновлень при запуску",
|
||||
"tryInferAppIdFromCode": "Спробувати вивести ідентифікатор застосунку з вихідного коду",
|
||||
"removeOnExternalUninstall": "Автоматично видаляти застосунки, які було видалено зовнішнім чином",
|
||||
"pickHighestVersionCode": "Автоматично вибрати APK з найвищим кодом версії",
|
||||
"checkUpdateOnDetailPage": "Перевіряти наявність оновлень при відкритті сторінки деталей застосунку",
|
||||
"disablePageTransitions": "Вимкнути анімації переходів між сторінками",
|
||||
"reversePageTransitions": "Зворотні анімації переходів між сторінками",
|
||||
"minStarCount": "Мінімальна кількість зірок",
|
||||
"addInfoBelow": "Додати цю інформацію нижче.",
|
||||
"addInfoInSettings": "Додати цю інформацію у налаштуваннях.",
|
||||
"githubSourceNote": "Лімітування швидкості GitHub можна уникнути, використовуючи ключ API.",
|
||||
"sortByLastLinkSegment": "Сортувати лише за останнім сегментом посилання",
|
||||
"filterReleaseNotesByRegEx": "Фільтрувати примітки до релізу за регулярним виразом",
|
||||
"customLinkFilterRegex": "Фільтр кастомного посилання на APK за регулярним виразом (за замовчуванням '.apk$')",
|
||||
"appsPossiblyUpdated": "Спроб оновлення застосунків",
|
||||
"appsPossiblyUpdatedNotifDescription": "Повідомляє користувача, що оновлення одного або декількох застосунків можливо були застосовані в фоновому режимі",
|
||||
"xWasPossiblyUpdatedToY": "{} можливо було оновлено до {}.",
|
||||
"enableBackgroundUpdates": "Увімкнути оновлення в фоновому режимі",
|
||||
"backgroundUpdateReqsExplanation": "Оновлення в фоновому режимі може бути неможливим для всіх застосунків.",
|
||||
"backgroundUpdateLimitsExplanation": "Успіх фонової установки може бути визначений лише після відкриття Obtainium.",
|
||||
"verifyLatestTag": "Перевірити тег 'latest'",
|
||||
"intermediateLinkRegex": "Фільтр для 'Проміжного' Посилання для Відвідування",
|
||||
"filterByLinkText": "Фільтрувати посилання за текстом посилання",
|
||||
"intermediateLinkNotFound": "Проміжне посилання не знайдено",
|
||||
"intermediateLink": "Проміжне посилання",
|
||||
"exemptFromBackgroundUpdates": "Виключено з фонових оновлень (якщо ввімкнено)",
|
||||
"bgUpdatesOnWiFiOnly": "Вимкнути фонові оновлення поза Wi-Fi",
|
||||
"autoSelectHighestVersionCode": "Автоматичний вибір APK з найвищим кодом версії",
|
||||
"versionExtractionRegEx": "Регулярний вираз для вилучення рядка версії",
|
||||
"matchGroupToUse": "Група співпадінь для використання в регулярному виразі вилучення версії",
|
||||
"highlightTouchTargets": "Підсвічувати менш очевидні області дотику",
|
||||
"pickExportDir": "Вибрати каталог експорту",
|
||||
"autoExportOnChanges": "Автоматичний експорт при змінах",
|
||||
"includeSettings": "Включити налаштування",
|
||||
"filterVersionsByRegEx": "Фільтрувати версії за регулярним виразом",
|
||||
"trySelectingSuggestedVersionCode": "Спробуйте вибрати запропонований код версії APK",
|
||||
"dontSortReleasesList": "Зберігати порядок випуску з API",
|
||||
"reverseSort": "Зворотне сортування",
|
||||
"takeFirstLink": "Вибрати перше посилання",
|
||||
"skipSort": "Пропустити сортування",
|
||||
"debugMenu": "Меню налагодження",
|
||||
"bgTaskStarted": "Запущено фонове завдання - перевірте журнали.",
|
||||
"runBgCheckNow": "Запустити перевірку оновлень в фоновому режимі зараз",
|
||||
"versionExtractWholePage": "Застосувати регулярний вираз вилучення версії до всієї сторінки",
|
||||
"installing": "Встановлення",
|
||||
"skipUpdateNotifications": "Пропустити сповіщення про оновлення",
|
||||
"updatesAvailableNotifChannel": "Доступні оновлення",
|
||||
"appsUpdatedNotifChannel": "Застосунки оновлені",
|
||||
"appsPossiblyUpdatedNotifChannel": "Спроба оновлення застосунків",
|
||||
"errorCheckingUpdatesNotifChannel": "Помилка перевірки оновлень",
|
||||
"appsRemovedNotifChannel": "Застосунки видалені",
|
||||
"downloadingXNotifChannel": "Завантаження {}",
|
||||
"completeAppInstallationNotifChannel": "Завершення встановлення застосунку",
|
||||
"checkingForUpdatesNotifChannel": "Перевірка оновлень",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Перевіряти лише встановлені та застосунки, які відстежуються для оновлень",
|
||||
"supportFixedAPKURL": "Підтримка фіксованих посилань на APK",
|
||||
"selectX": "Вибрати {}",
|
||||
"parallelDownloads": "Дозволити паралельні завантаження",
|
||||
"installMethod": "Метод встановлення",
|
||||
"normal": "Звичайний",
|
||||
"root": "Root",
|
||||
"shizukuBinderNotFound": "Сумісний сервіс Shizuku не було знайдено",
|
||||
"useSystemFont": "Використовувати системний шрифт",
|
||||
"systemFontError": "Помилка завантаження системного шрифту: {}",
|
||||
"useVersionCodeAsOSVersion": "Використовувати код версії застосунку як версію, визначену операційною системою",
|
||||
"requestHeader": "Заголовок запиту",
|
||||
"useLatestAssetDateAsReleaseDate": "Використовувати останню дату завантаження ресурсу як дату випуску",
|
||||
"defaultPseudoVersioningMethod": "Метод за замовчуванням псевдо-версіонування",
|
||||
"partialAPKHash": "Хеш часткового APK",
|
||||
"APKLinkHash": "Хеш посилання на APK",
|
||||
"directAPKLink": "Пряме посилання на APK",
|
||||
"pseudoVersionInUse": "Використовується псевдо-версія",
|
||||
"installed": "Встановлено",
|
||||
"latest": "Остання",
|
||||
"invertRegEx": "Інвертувати регулярний вираз",
|
||||
"note": "Примітка",
|
||||
"selfHostedNote": "Випадаючий список \"{}\" може використовуватися для доступу до власних/призначених для самостійного використання екземплярів будь-якого джерела.",
|
||||
"badDownload": "APK не вдалося розпарсити (несумісний або часткове завантаження)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Діліться новими додатками з AppVerifier (якщо доступно)",
|
||||
"appVerifierInstructionToast": "Надішліть на AppVerifier, а потім поверніться сюди, коли будете готові.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Видалити застосунок?",
|
||||
"other": "Видалити застосунки?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Забагато запитів (обмеження швидкості) - повторіть спробу через {} хвилину",
|
||||
"other": "Забагато запитів (обмеження швидкості) - повторіть спробу через {} хвилин"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "Помилка перевірки оновлень у фоновому режимі - спробую знову через {} хвилину",
|
||||
"other": "Помилка перевірки оновлень у фоновому режимі - спробую знову через {} хвилин"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "Фонова перевірка оновлень знайшла {} оновлення - сповістити користувача, якщо це необхідно",
|
||||
"other": "Фонова перевірка оновлень знайшла {} оновлень - сповістити користувача, якщо це необхідно"
|
||||
},
|
||||
"apps": {
|
||||
"one": "{} застосунок",
|
||||
"other": "{} застосунки"
|
||||
},
|
||||
"url": {
|
||||
"one": "{} URL-адреса",
|
||||
"other": "{} URL-адреси"
|
||||
},
|
||||
"minute": {
|
||||
"one": "{} хвилина",
|
||||
"other": "{} хвилин"
|
||||
},
|
||||
"hour": {
|
||||
"one": "{} година",
|
||||
"other": "{} годин"
|
||||
},
|
||||
"day": {
|
||||
"one": "{} день",
|
||||
"other": "{} днів"
|
||||
},
|
||||
"clearedNLogsBeforeXAfterY": {
|
||||
"one": "Очищено {n} журнал (до = {before}, після = {after})",
|
||||
"other": "Очищено {n} журналів (до = {before}, після = {after})"
|
||||
},
|
||||
"xAndNMoreUpdatesAvailable": {
|
||||
"one": "{} та ще 1 застосунок мають оновлення.",
|
||||
"other": "{} та ще {} застосунки мають оновлення."
|
||||
},
|
||||
"xAndNMoreUpdatesInstalled": {
|
||||
"one": "{} та ще 1 застосунок було оновлено.",
|
||||
"other": "{} та ще {} застосунків було оновлено."
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} та ще 1 застосунок можливо було оновлено.",
|
||||
"other": "{} та ще {} застосунків можливо було оновлено."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APK-файли"
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@
|
||||
"unpinFromTop": "Bỏ ghim khỏi đầu trang",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Đặt lại trạng thái cài đặt cho ứng dụng đã chọn?",
|
||||
"installStatusOfXWillBeResetExplanation": "Trạng thái cài đặt của mọi Ứng dụng đã chọn sẽ được đặt lại.\n\nĐiều này có thể hữu ích khi phiên bản Ứng dụng hiển thị trong Obtainium không chính xác do cập nhật không thành công hoặc các sự cố khác.",
|
||||
"customLinkMessage": "Các liên kết này hoạt động trên các thiết bị có cài đặt Gainium",
|
||||
"customLinkMessage": "Các liên kết này hoạt động trên các thiết bị có cài đặt Obtainium",
|
||||
"shareAppConfigLinks": "Chia sẻ cấu hình ứng dụng dưới dạng liên kết HTML",
|
||||
"shareSelectedAppURLs": "Chia sẻ URL ứng dụng đã chọn",
|
||||
"resetInstallStatus": "Đặt lại trạng thái cài đặt",
|
||||
@ -80,14 +80,13 @@
|
||||
"removeOutdatedFilter": "Xóa bộ lọc ứng dụng lỗi thời",
|
||||
"showOutdatedOnly": "Chỉ hiển thị các ứng dụng lỗi thời",
|
||||
"filter": "Lọc",
|
||||
"filterActive": "Lọc *",
|
||||
"filterApps": "Lọc ứng dụng",
|
||||
"appName": "Tên ứng dụng",
|
||||
"author": "Tác giả",
|
||||
"upToDateApps": "Ứng dụng cập nhật",
|
||||
"nonInstalledApps": "Ứng dụng chưa được cài đặt",
|
||||
"importExport": "Nhập/Xuất",
|
||||
"settings": "Cài đặt",
|
||||
"settings": "Thiết đặt",
|
||||
"exportedTo": "Đã xuất sang {}",
|
||||
"obtainiumExport": "Xuất",
|
||||
"invalidInput": "Đầu vào không hợp lệ",
|
||||
@ -132,7 +131,7 @@
|
||||
"close": "Đóng",
|
||||
"share": "Chia sẻ",
|
||||
"appNotFound": "Không tìm thấy ứng dụng",
|
||||
"obtainiumExportHyphenatedLowercase": "xuất khẩu-obtainium",
|
||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
||||
"pickAnAPK": "Chọn một APK",
|
||||
"appHasMoreThanOnePackage": "{} có nhiều gói:",
|
||||
"deviceSupportsXArch": "Thiết bị của bạn hỗ trợ kiến trúc CPU {}.",
|
||||
@ -168,7 +167,7 @@
|
||||
"lastUpdateCheckX": "Kiểm tra cập nhật lần cuối: {}",
|
||||
"remove": "Loại bỏ",
|
||||
"yesMarkUpdated": "Có, Đánh dấu là đã cập nhật",
|
||||
"fdroid": "Chính thức của F-Droid",
|
||||
"fdroid": "F-Droid Chính thức",
|
||||
"appIdOrName": "ID hoặc tên ứng dụng",
|
||||
"appId": "ID ứng dụng",
|
||||
"appWithIdOrNameNotFound": "Không tìm thấy ứng dụng nào có ID hoặc tên đó",
|
||||
@ -188,18 +187,18 @@
|
||||
"noAPKFound": "Không tìm thấy APK",
|
||||
"noVersionDetection": "Không phát hiện phiên bản",
|
||||
"categorize": "Phân loại",
|
||||
"categories": "Thể loại",
|
||||
"category": "Thể loại",
|
||||
"noCategory": "Không thể loại",
|
||||
"noCategories": "Không thể loại",
|
||||
"deleteCategoriesQuestion": "Xóa thể loại?",
|
||||
"categoryDeleteWarning": "Tất cả ứng dụng trong thể loại đã xóa sẽ được đặt thành chưa được phân loại.",
|
||||
"categories": "Danh mục",
|
||||
"category": "Danh mục",
|
||||
"noCategory": "Không danh mục",
|
||||
"noCategories": "Không danh mục",
|
||||
"deleteCategoriesQuestion": "Xóa danh mục?",
|
||||
"categoryDeleteWarning": "Tất cả ứng dụng trong danh mục đã xóa sẽ được đặt thành chưa được phân loại.",
|
||||
"addCategory": "Thêm thể loại",
|
||||
"label": "Nhãn",
|
||||
"language": "Ngôn ngữ",
|
||||
"copiedToClipboard": "Sao chép vào clipboard",
|
||||
"storagePermissionDenied": "Quyền lưu trữ bị từ chối",
|
||||
"selectedCategorizeWarning": "Điều này sẽ thay thế mọi cài đặt thể loại hiện có cho Ứng dụng đã chọn.",
|
||||
"selectedCategorizeWarning": "Điều này sẽ thay thế mọi thiết đặt danh mục hiện có cho Ứng dụng đã chọn.",
|
||||
"filterAPKsByRegEx": "Lọc APK theo biểu thức chính quy",
|
||||
"removeFromObtainium": "Loại khỏi Obtainium",
|
||||
"uninstallFromDevice": "Gỡ cài đặt khỏi thiết bị",
|
||||
@ -212,16 +211,16 @@
|
||||
"versionDetectionExplanation": "Đối chiếu chuỗi phiên bản với phiên bản được phát hiện từ hệ điều hành",
|
||||
"versionDetection": "Phát hiện phiên bản",
|
||||
"standardVersionDetection": "Phát hiện phiên bản tiêu chuẩn",
|
||||
"groupByCategory": "Nhóm theo thể loại",
|
||||
"groupByCategory": "Nhóm theo danh mục",
|
||||
"autoApkFilterByArch": "Cố gắng lọc APK theo kiến trúc CPU nếu có thể",
|
||||
"overrideSource": "Ghi đè nguồn",
|
||||
"dontShowAgain": "Đừng hiển thị thông tin này nữa",
|
||||
"dontShowTrackOnlyWarnings": "Không hiển thị cảnh báo 'Chỉ theo dõi'",
|
||||
"dontShowAPKOriginWarnings": "Không hiển thị cảnh báo nguồn gốc APK",
|
||||
"moveNonInstalledAppsToBottom": "Chuyển Ứng dụng chưa được cài đặt xuống cuối danh sách",
|
||||
"gitlabPATLabel": "GitLab Token\n(Cho phép tìm kiếm và lọc APK tốt hơn)",
|
||||
"gitlabPATLabel": "GitLab Token",
|
||||
"about": "Giới thiệu",
|
||||
"requiresCredentialsInSettings": "{}: Điều này cần thông tin xác thực bổ sung (trong Cài đặt)",
|
||||
"requiresCredentialsInSettings": "{}: Điều này cần thông tin xác thực bổ sung (trong Thiết đặt)",
|
||||
"checkOnStart": "Kiểm tra các bản cập nhật khi khởi động",
|
||||
"tryInferAppIdFromCode": "Thử suy ra ID ứng dụng từ mã nguồn",
|
||||
"removeOnExternalUninstall": "Tự động xóa ứng dụng đã gỡ cài đặt bên ngoài",
|
||||
@ -231,9 +230,8 @@
|
||||
"reversePageTransitions": "Hoạt ảnh chuyển đổi trang đảo ngược",
|
||||
"minStarCount": "Số lượng sao tối thiểu",
|
||||
"addInfoBelow": "Thêm thông tin này vào bên dưới.",
|
||||
"addInfoInSettings": "Thêm thông tin này vào Cài đặt.",
|
||||
"addInfoInSettings": "Thêm thông tin này vào Thiết đặt.",
|
||||
"githubSourceNote": "Có thể tránh được việc giới hạn tốc độ GitHub bằng cách sử dụng khóa API.",
|
||||
"gitlabSourceNote": "Trích xuất APK GitLab có thể không hoạt động nếu không có khóa API.",
|
||||
"sortByLastLinkSegment": "Chỉ sắp xếp theo đoạn cuối của liên kết",
|
||||
"filterReleaseNotesByRegEx": "Lọc ghi chú phát hành theo biểu thức chính quy",
|
||||
"customLinkFilterRegex": "Bộ lọc liên kết APK tùy chỉnh theo biểu thức chính quy (Mặc định '.apk$')",
|
||||
@ -256,7 +254,7 @@
|
||||
"highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn",
|
||||
"pickExportDir": "Chọn thư mục xuất",
|
||||
"autoExportOnChanges": "Tự động xuất",
|
||||
"includeSettings": "Bao gồm cài đặt ứng dụng",
|
||||
"includeSettings": "Bao gồm thiết đặt",
|
||||
"filterVersionsByRegEx": "Lọc phiên bản theo biểu thức chính quy",
|
||||
"trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
|
||||
"dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
|
||||
@ -298,9 +296,11 @@
|
||||
"installed": "Đã cài đặt",
|
||||
"latest": "Mới nhất",
|
||||
"invertRegEx": "Đảo ngược biểu thức chính quy",
|
||||
"note": "Note",
|
||||
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||
"badDownload": "The APK could not be parsed (incompatible or partial download)",
|
||||
"note": "Ghi chú",
|
||||
"selfHostedNote": "Trình đơn thả xuống \"{}\" có thể được dùng để tiếp cận các phiên bản tự lưu trữ/tùy chỉnh của bất kỳ nguồn nào.",
|
||||
"badDownload": "Không thể phân tích cú pháp APK (tải xuống một phần hoặc không tương thích)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Share new Apps with AppVerifier (if available)",
|
||||
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Gỡ ứng dụng?",
|
||||
"other": "Gỡ ứng dụng?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} và 1 ứng dụng khác có thể đã được cập nhật.",
|
||||
"other": "{} và {} ứng dụng khác có thể đã được cập nhật."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@
|
||||
"pleaseWait": "请稍候",
|
||||
"updateAvailable": "更新可用",
|
||||
"notInstalled": "未安装",
|
||||
"pseudoVersion": "伪版本",
|
||||
"pseudoVersion": "虚拟版本号",
|
||||
"selectAll": "全选",
|
||||
"deselectX": "取消选择 {}",
|
||||
"xWillBeRemovedButRemainInstalled": "“{}”将从 Obtainium 中删除,但仍安装在您的设备中。",
|
||||
@ -80,7 +80,6 @@
|
||||
"removeOutdatedFilter": "删除失效的应用筛选",
|
||||
"showOutdatedOnly": "只显示待更新应用",
|
||||
"filter": "筛选",
|
||||
"filterActive": "筛选 *",
|
||||
"filterApps": "筛选应用",
|
||||
"appName": "应用名称",
|
||||
"author": "作者",
|
||||
@ -209,7 +208,7 @@
|
||||
"changes": "更新日志",
|
||||
"releaseDate": "发行日期",
|
||||
"importFromURLsInFile": "从文件中的 URL 导入(如 OPML)",
|
||||
"versionDetectionExplanation": "将版本字符串与操作系统检测到的版本进行协调",
|
||||
"versionDetectionExplanation": "使发行版本号与应用定义的版本号一致",
|
||||
"versionDetection": "版本检测",
|
||||
"standardVersionDetection": "常规版本检测",
|
||||
"groupByCategory": "按类别分组显示",
|
||||
@ -219,13 +218,13 @@
|
||||
"dontShowTrackOnlyWarnings": "忽略“仅追踪”模式警告",
|
||||
"dontShowAPKOriginWarnings": "忽略 APK 文件来源警告",
|
||||
"moveNonInstalledAppsToBottom": "将未安装应用置底",
|
||||
"gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)",
|
||||
"gitlabPATLabel": "GitLab 个人访问令牌",
|
||||
"about": "相关文档",
|
||||
"requiresCredentialsInSettings": "{}:此功能需要额外的凭据(在“设置”中添加)",
|
||||
"checkOnStart": "启动时进行一次检查",
|
||||
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
|
||||
"removeOnExternalUninstall": "自动删除列表中已卸载的应用",
|
||||
"pickHighestVersionCode": "自动选择版本号最高的 APK 文件",
|
||||
"pickHighestVersionCode": "自动选取内部版本号最高的 APK 文件",
|
||||
"checkUpdateOnDetailPage": "打开应用详情页时进行检查",
|
||||
"disablePageTransitions": "禁用页面过渡动画效果",
|
||||
"reversePageTransitions": "反转页面过渡动画效果",
|
||||
@ -233,7 +232,6 @@
|
||||
"addInfoBelow": "在下方添加此凭据。",
|
||||
"addInfoInSettings": "在“设置”中添加此凭据。",
|
||||
"githubSourceNote": "使用访问令牌可避免触发 GitHub 的 API 请求限制。",
|
||||
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
|
||||
"sortByLastLinkSegment": "仅根据链接的末尾部分进行筛选",
|
||||
"filterReleaseNotesByRegEx": "筛选发行说明(正则表达式)",
|
||||
"customLinkFilterRegex": "筛选自定义来源的 APK 文件链接\n(正则表达式,默认匹配模式为“.apk$”)",
|
||||
@ -250,7 +248,7 @@
|
||||
"intermediateLink": "中转链接",
|
||||
"exemptFromBackgroundUpdates": "禁用后台更新(如果已经全局启用)",
|
||||
"bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
|
||||
"autoSelectHighestVersionCode": "自动选择版本号最高的 APK 文件",
|
||||
"autoSelectHighestVersionCode": "自动选择内部版本号最高的 APK 文件",
|
||||
"versionExtractionRegEx": "版本号提取规则(正则表达式)",
|
||||
"matchGroupToUse": "引用的捕获组",
|
||||
"highlightTouchTargets": "突出展示不明显的触摸区域",
|
||||
@ -287,20 +285,22 @@
|
||||
"shizukuBinderNotFound": "未发现兼容的 Shizuku 服务",
|
||||
"useSystemFont": "使用系统字体",
|
||||
"systemFontError": "加载系统字体出错:{}",
|
||||
"useVersionCodeAsOSVersion": "使用应用程序版本代码作为操作系统检测到的版本",
|
||||
"useVersionCodeAsOSVersion": "使用内部版本号代替应用定义的版本号",
|
||||
"requestHeader": "请求标头",
|
||||
"useLatestAssetDateAsReleaseDate": "使用最新资产上传作为发布日期",
|
||||
"defaultPseudoVersioningMethod": "默认伪版本控制方法",
|
||||
"partialAPKHash": "部分 APK 哈希值",
|
||||
"APKLinkHash": "APK 链接哈希",
|
||||
"directAPKLink": "直接 APK 链接",
|
||||
"pseudoVersionInUse": "伪版本正在使用",
|
||||
"installed": "已安装",
|
||||
"latest": "最新的",
|
||||
"invertRegEx": "反转正则表达式",
|
||||
"useLatestAssetDateAsReleaseDate": "使用最近文件上传时间作为发行日期",
|
||||
"defaultPseudoVersioningMethod": "默认虚拟版本方案",
|
||||
"partialAPKHash": "APK 文件散列值片段",
|
||||
"APKLinkHash": "APK 文件链接散列值",
|
||||
"directAPKLink": "APK 文件直链",
|
||||
"pseudoVersionInUse": "正在使用虚拟版本号",
|
||||
"installed": "当前版本",
|
||||
"latest": "最新版本",
|
||||
"invertRegEx": "反转匹配",
|
||||
"note": "备注",
|
||||
"selfHostedNote": "{}\"下拉菜单可用于访问任何来源的自托管/自定义实例。",
|
||||
"badDownload": "无法解析 APK(不兼容或部分下载)",
|
||||
"selfHostedNote": "可以通过“{}”下拉菜单来指向任意来源的自托管/自定义实例。",
|
||||
"badDownload": "无法解析 APK 文件(不兼容或文件不完整)",
|
||||
"beforeNewInstallsShareToAppVerifier": "与 AppVerifier 共享新应用程序(如有)",
|
||||
"appVerifierInstructionToast": "分享到 AppVerifier,准备就绪后返回此处。",
|
||||
"removeAppQuestion": {
|
||||
"one": "是否删除应用?",
|
||||
"other": "是否删除应用?"
|
||||
@ -352,5 +352,9 @@
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} 和另外 1 个应用已尝试更新。",
|
||||
"other": "“{}”和另外 {} 个应用已尝试更新。"
|
||||
},
|
||||
"apk": {
|
||||
"one": "{}APK",
|
||||
"other": "{}APK"
|
||||
}
|
||||
}
|
||||
|
@ -76,16 +76,7 @@ class FDroid extends AppSource {
|
||||
'https://$host/repo/$appId',
|
||||
standardUrl,
|
||||
name,
|
||||
autoSelectHighestVersionCode:
|
||||
additionalSettings['autoSelectHighestVersionCode'] == true,
|
||||
trySelectingSuggestedVersionCode:
|
||||
additionalSettings['trySelectingSuggestedVersionCode'] == true,
|
||||
filterVersionsByRegEx:
|
||||
(additionalSettings['filterVersionsByRegEx'] as String?)
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? additionalSettings['filterVersionsByRegEx']
|
||||
: null);
|
||||
additionalSettings: additionalSettings);
|
||||
if (!hostChanged) {
|
||||
try {
|
||||
var res = await sourceRequest(
|
||||
@ -166,12 +157,30 @@ class FDroid extends AppSource {
|
||||
|
||||
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
|
||||
{bool autoSelectHighestVersionCode = false,
|
||||
bool trySelectingSuggestedVersionCode = false,
|
||||
String? filterVersionsByRegEx}) {
|
||||
{Map<String, dynamic> additionalSettings = const {}}) {
|
||||
var autoSelectHighestVersionCode =
|
||||
additionalSettings['autoSelectHighestVersionCode'] == true;
|
||||
var trySelectingSuggestedVersionCode =
|
||||
additionalSettings['trySelectingSuggestedVersionCode'] == true;
|
||||
var filterVersionsByRegEx =
|
||||
(additionalSettings['filterVersionsByRegEx'] as String?)?.isNotEmpty ==
|
||||
true
|
||||
? additionalSettings['filterVersionsByRegEx']
|
||||
: null;
|
||||
var apkFilterRegEx =
|
||||
(additionalSettings['apkFilterRegEx'] as String?)?.isNotEmpty == true
|
||||
? additionalSettings['apkFilterRegEx']
|
||||
: null;
|
||||
if (res.statusCode == 200) {
|
||||
var response = jsonDecode(res.body);
|
||||
List<dynamic> releases = response['packages'] ?? [];
|
||||
if (apkFilterRegEx != null) {
|
||||
releases = releases.where((rel) {
|
||||
String apk = '${apkUrlPrefix}_${rel['versionCode']}.apk';
|
||||
return filterApks([MapEntry(apk, apk)], apkFilterRegEx, false)
|
||||
.isNotEmpty;
|
||||
}).toList();
|
||||
}
|
||||
if (releases.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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';
|
||||
@ -45,7 +46,7 @@ class FDroidRepo extends AppSource {
|
||||
String sourceSpecificStandardizeURL(String url) {
|
||||
var standardUri = Uri.parse(url);
|
||||
var pathSegments = standardUri.pathSegments;
|
||||
if (pathSegments.last == 'index.xml') {
|
||||
if (pathSegments.isNotEmpty && pathSegments.last == 'index.xml') {
|
||||
pathSegments.removeLast();
|
||||
standardUri = standardUri.replace(path: pathSegments.join('/'));
|
||||
}
|
||||
@ -60,7 +61,7 @@ class FDroidRepo extends AppSource {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
url = removeQueryParamsFromUrl(standardizeUrl(url));
|
||||
var res = await sourceRequest('$url/index.xml', {});
|
||||
var res = await sourceRequestWithURLVariants(url, {});
|
||||
if (res.statusCode == 200) {
|
||||
var body = parse(res.body);
|
||||
Map<String, List<String>> results = {};
|
||||
@ -72,7 +73,11 @@ class FDroidRepo extends AppSource {
|
||||
appId.contains(query) ||
|
||||
appName.contains(query) ||
|
||||
appDesc.contains(query)) {
|
||||
results['$url?appId=$appId'] = [appName, appDesc];
|
||||
results[
|
||||
'${res.request!.url.toString().split('/').reversed.toList().sublist(1).reversed.join('/')}?appId=$appId'] = [
|
||||
appName,
|
||||
appDesc
|
||||
];
|
||||
}
|
||||
});
|
||||
return results;
|
||||
@ -102,6 +107,26 @@ class FDroidRepo extends AppSource {
|
||||
return app;
|
||||
}
|
||||
|
||||
Future<Response> sourceRequestWithURLVariants(
|
||||
String url,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
var res = await sourceRequest(
|
||||
'$url${url.endsWith('/index.xml') ? '' : '/index.xml'}',
|
||||
additionalSettings);
|
||||
if (res.statusCode != 200) {
|
||||
var base = url.endsWith('/index.xml')
|
||||
? url.split('/').reversed.toList().sublist(1).reversed.join('/')
|
||||
: url;
|
||||
res = await sourceRequest('$base/repo/index.xml', additionalSettings);
|
||||
if (res.statusCode != 200) {
|
||||
res = await sourceRequest(
|
||||
'$base/fdroid/repo/index.xml', additionalSettings);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<APKDetails> getLatestAPKDetails(
|
||||
String standardUrl,
|
||||
@ -117,9 +142,8 @@ class FDroidRepo extends AppSource {
|
||||
if (appIdOrName == null) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
var res = await sourceRequest(
|
||||
'$standardUrl${standardUrl.endsWith('/index.xml') ? '' : '/index.xml'}',
|
||||
additionalSettings);
|
||||
var res =
|
||||
await sourceRequestWithURLVariants(standardUrl, additionalSettings);
|
||||
if (res.statusCode == 200) {
|
||||
var body = parse(res.body);
|
||||
var foundApps = body.querySelectorAll('application').where((element) {
|
||||
@ -168,7 +192,8 @@ class FDroidRepo extends AppSource {
|
||||
latestVersionReleases = [latestVersionReleases[0]];
|
||||
}
|
||||
List<String> apkUrls = latestVersionReleases
|
||||
.map((e) => '$standardUrl/${e.querySelector('apkname')!.innerHtml}')
|
||||
.map((e) =>
|
||||
'${res.request!.url.toString().split('/').reversed.toList().sublist(1).reversed.join('/')}/${e.querySelector('apkname')!.innerHtml}')
|
||||
.toList();
|
||||
return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls),
|
||||
AppNames(authorName, appName),
|
||||
|
@ -344,12 +344,14 @@ class GitHub extends AppSource {
|
||||
});
|
||||
}
|
||||
if (latestRelease != null &&
|
||||
(latestRelease['tag_name'] ?? latestRelease['name']) != null &&
|
||||
releases.isNotEmpty &&
|
||||
latestRelease !=
|
||||
(releases[releases.length - 1]['tag_name'] ??
|
||||
releases[0]['name'])) {
|
||||
var ind = releases.indexWhere((element) =>
|
||||
latestRelease == (element['tag_name'] ?? element['name']));
|
||||
(latestRelease['tag_name'] ?? latestRelease['name']) ==
|
||||
(element['tag_name'] ?? element['name']));
|
||||
if (ind >= 0) {
|
||||
releases.add(releases.removeAt(ind));
|
||||
}
|
||||
@ -400,7 +402,7 @@ class GitHub extends AppSource {
|
||||
if (version == null) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
var changeLog = targetRelease['body'].toString();
|
||||
var changeLog = (targetRelease['body'] ?? '').toString();
|
||||
return APKDetails(
|
||||
version,
|
||||
targetRelease['apkUrls'] as List<MapEntry<String, String>>,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:obtainium/app_sources/github.dart';
|
||||
import 'package:obtainium/custom_errors.dart';
|
||||
@ -72,14 +72,6 @@ class GitLab extends AppSource {
|
||||
return creds != null && creds.isNotEmpty ? creds : null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> getSourceNote() async {
|
||||
if ((await getPATIfAny({})) == null) {
|
||||
return '${tr('gitlabSourceNote')} ${hostChanged ? tr('addInfoBelow') : tr('addInfoInSettings')}';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, List<String>>> search(String query,
|
||||
{Map<String, dynamic> querySettings = const {}}) async {
|
||||
@ -104,23 +96,43 @@ class GitLab extends AppSource {
|
||||
String? changeLogPageFromStandardUrl(String standardUrl) =>
|
||||
'$standardUrl/-/releases';
|
||||
|
||||
@override
|
||||
Future<Map<String, String>?> getRequestHeaders(
|
||||
Map<String, dynamic> additionalSettings,
|
||||
{bool forAPKDownload = false}) async {
|
||||
// Change headers to pacify, e.g. cloudflare protection
|
||||
// Related to: (#1397, #1389, #1384, #1382, #1381, #1380, #1359, #854, #785, #697)
|
||||
var headers = <String, String>{};
|
||||
headers[HttpHeaders.refererHeader] = 'https://${hosts[0]}';
|
||||
if (headers.isNotEmpty) {
|
||||
return headers;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<APKDetails> getLatestAPKDetails(
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
bool fallbackToOlderReleases =
|
||||
additionalSettings['fallbackToOlderReleases'] == true;
|
||||
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
|
||||
Iterable<APKDetails> apkDetailsList = [];
|
||||
if (PAT != null) {
|
||||
// Prepare request params
|
||||
var names = GitHub().getAppNames(standardUrl);
|
||||
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
|
||||
String optionalAuth = (PAT != null) ? 'private_token=$PAT' : '';
|
||||
|
||||
bool trackOnly = additionalSettings['trackOnly'] == true;
|
||||
|
||||
// Request data from REST API
|
||||
Response res = await sourceRequest(
|
||||
'https://${hosts[0]}/api/v4/projects/${names.author}%2F${names.name}/releases?private_token=$PAT',
|
||||
'https://${hosts[0]}/api/v4/projects/${names.author}%2F${names.name}/${trackOnly ? 'repository/tags' : 'releases'}?$optionalAuth',
|
||||
additionalSettings);
|
||||
if (res.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
||||
// Extract .apk details from received data
|
||||
Iterable<APKDetails> apkDetailsList = [];
|
||||
var json = jsonDecode(res.body) as List<dynamic>;
|
||||
apkDetailsList = json.map((e) {
|
||||
var apkUrlsFromAssets = (e['assets']?['links'] as List<dynamic>? ?? [])
|
||||
@ -142,67 +154,32 @@ class GitLab extends AppSource {
|
||||
var apkUrlsSet = apkUrlsFromAssets.toSet();
|
||||
apkUrlsSet.addAll(uploadedAPKsFromDescription);
|
||||
var releaseDateString = e['released_at'] ?? e['created_at'];
|
||||
DateTime? releaseDate = releaseDateString != null
|
||||
? DateTime.parse(releaseDateString)
|
||||
: null;
|
||||
DateTime? releaseDate =
|
||||
releaseDateString != null ? DateTime.parse(releaseDateString) : null;
|
||||
return APKDetails(
|
||||
e['tag_name'] ?? e['name'],
|
||||
getApkUrlsFromUrls(apkUrlsSet.toList()),
|
||||
GitHub().getAppNames(standardUrl),
|
||||
releaseDate: releaseDate);
|
||||
});
|
||||
} else {
|
||||
Response res = await sourceRequest(
|
||||
'$standardUrl/-/tags?format=atom', additionalSettings);
|
||||
if (res.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
var standardUri = Uri.parse(standardUrl);
|
||||
var parsedHtml = parse(res.body);
|
||||
apkDetailsList = parsedHtml.querySelectorAll('entry').map((entry) {
|
||||
var entryContent = parse(
|
||||
parseFragment(entry.querySelector('content')!.innerHtml).text);
|
||||
var apkUrls = [
|
||||
...getLinksFromParsedHTML(
|
||||
entryContent,
|
||||
RegExp(
|
||||
'^${standardUri.path.replaceAllMapped(RegExp(r'[.*+?^${}()|[\]\\]'), (x) {
|
||||
return '\\${x[0]}';
|
||||
})}/uploads/[^/]+/[^/]+\\.apk\$',
|
||||
caseSensitive: false),
|
||||
standardUri.origin),
|
||||
// GitLab releases may contain links to externally hosted APKs
|
||||
...getLinksFromParsedHTML(entryContent,
|
||||
RegExp('/[^/]+\\.apk\$', caseSensitive: false), '')
|
||||
.where((element) => Uri.parse(element).host != '')
|
||||
];
|
||||
var entryId = entry.querySelector('id')?.innerHtml;
|
||||
var version =
|
||||
entryId == null ? null : Uri.parse(entryId).pathSegments.last;
|
||||
var releaseDateString = entry.querySelector('updated')?.innerHtml;
|
||||
DateTime? releaseDate = releaseDateString != null
|
||||
? DateTime.parse(releaseDateString)
|
||||
: null;
|
||||
if (version == null) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
return APKDetails(version, getApkUrlsFromUrls(apkUrls),
|
||||
GitHub().getAppNames(standardUrl),
|
||||
releaseDate: releaseDate);
|
||||
});
|
||||
}
|
||||
if (apkDetailsList.isEmpty) {
|
||||
throw NoReleasesError(note: tr('gitlabSourceNote'));
|
||||
throw NoReleasesError();
|
||||
}
|
||||
if (fallbackToOlderReleases) {
|
||||
if (additionalSettings['trackOnly'] != true) {
|
||||
var finalResult = apkDetailsList.first;
|
||||
|
||||
// Fallback procedure
|
||||
bool fallbackToOlderReleases =
|
||||
additionalSettings['fallbackToOlderReleases'] == true;
|
||||
if (finalResult.apkUrls.isEmpty && fallbackToOlderReleases && !trackOnly) {
|
||||
apkDetailsList =
|
||||
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
|
||||
finalResult = apkDetailsList.first;
|
||||
}
|
||||
if (apkDetailsList.isEmpty) {
|
||||
throw NoReleasesError(note: tr('gitlabSourceNote'));
|
||||
}
|
||||
|
||||
if (finalResult.apkUrls.isEmpty && !trackOnly) {
|
||||
throw NoAPKError();
|
||||
}
|
||||
|
||||
return apkDetailsList.first;
|
||||
}
|
||||
}
|
||||
|
@ -50,10 +50,6 @@ class IzzyOnDroid extends AppSource {
|
||||
'https://android.izzysoft.de/frepo/$appId',
|
||||
standardUrl,
|
||||
name,
|
||||
autoSelectHighestVersionCode:
|
||||
additionalSettings['autoSelectHighestVersionCode'] == true,
|
||||
trySelectingSuggestedVersionCode:
|
||||
additionalSettings['trySelectingSuggestedVersionCode'] == true,
|
||||
filterVersionsByRegEx: additionalSettings['filterVersionsByRegEx']);
|
||||
additionalSettings: additionalSettings);
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ List<MapEntry<Locale, String>> supportedLocales = const [
|
||||
MapEntry(Locale('nl'), 'Nederlands'),
|
||||
MapEntry(Locale('vi'), 'Tiếng Việt'),
|
||||
MapEntry(Locale('tr'), 'Türkçe'),
|
||||
MapEntry(Locale('uk'), 'Українська'),
|
||||
];
|
||||
const fallbackLocale = Locale('en');
|
||||
const localeDir = 'assets/translations';
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:obtainium/app_sources/html.dart';
|
||||
import 'package:obtainium/components/custom_app_bar.dart';
|
||||
import 'package:obtainium/components/generated_form.dart';
|
||||
import 'package:obtainium/components/generated_form_modal.dart';
|
||||
@ -62,18 +61,6 @@ class AddAppPageState extends State<AddAppPage> {
|
||||
var prevHost = pickedSource?.hosts.isNotEmpty == true
|
||||
? pickedSource?.hosts[0]
|
||||
: null;
|
||||
try {
|
||||
var naturalSource =
|
||||
valid ? sourceProvider.getSource(userInput) : null;
|
||||
if (naturalSource != null &&
|
||||
naturalSource.runtimeType.toString() !=
|
||||
HTML().runtimeType.toString()) {
|
||||
// If input has changed to match a regular source, reset the override
|
||||
pickedSourceOverride = null;
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
var source = valid
|
||||
? sourceProvider.getSource(userInput,
|
||||
overrideSource: pickedSourceOverride)
|
||||
@ -163,7 +150,7 @@ class AddAppPageState extends State<AddAppPage> {
|
||||
app = await sourceProvider.getApp(
|
||||
pickedSource!, userInput.trim(), additionalSettings,
|
||||
trackOnlyOverride: trackOnly,
|
||||
overrideSource: pickedSourceOverride,
|
||||
sourceIsOverriden: pickedSourceOverride != null,
|
||||
inferAppIdIfOptional: inferAppIdIfOptional);
|
||||
// Only download the APK here if you need to for the package ID
|
||||
if (isTempId(app) && app.additionalSettings['trackOnly'] != true) {
|
||||
@ -361,8 +348,9 @@ class AddAppPageState extends State<AddAppPage> {
|
||||
[
|
||||
GeneratedFormDropdown(
|
||||
'overrideSource',
|
||||
defaultValue: HTML().runtimeType.toString(),
|
||||
defaultValue: '',
|
||||
[
|
||||
MapEntry('', tr('none')),
|
||||
...sourceProvider.sources.map(
|
||||
(s) => MapEntry(s.runtimeType.toString(), s.name))
|
||||
],
|
||||
@ -577,11 +565,7 @@ class AddAppPageState extends State<AddAppPage> {
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
if (pickedSourceOverride != null ||
|
||||
(pickedSource != null &&
|
||||
pickedSource.runtimeType.toString() ==
|
||||
HTML().runtimeType.toString()))
|
||||
getHTMLSourceOverrideDropdown(),
|
||||
if (pickedSource != null) getHTMLSourceOverrideDropdown(),
|
||||
if (shouldShowSearchBar()) getSearchBarRow(),
|
||||
if (pickedSource != null)
|
||||
FutureBuilder(
|
||||
|
@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:obtainium/components/generated_form_modal.dart';
|
||||
import 'package:obtainium/custom_errors.dart';
|
||||
import 'package:obtainium/main.dart';
|
||||
import 'package:obtainium/pages/apps.dart';
|
||||
import 'package:obtainium/pages/settings.dart';
|
||||
import 'package:obtainium/providers/apps_provider.dart';
|
||||
import 'package:obtainium/providers/settings_provider.dart';
|
||||
@ -104,6 +105,11 @@ class _AppPageState extends State<AppPage> {
|
||||
if (installedVersionIsEstimate) {
|
||||
infoLines = '${tr('pseudoVersionInUse')}\n$infoLines';
|
||||
}
|
||||
if ((app?.app.apkUrls.length ?? 0) > 0) {
|
||||
infoLines =
|
||||
'$infoLines\n${app?.app.apkUrls.length == 1 ? app?.app.apkUrls[0].key : plural('apk', app?.app.apkUrls.length ?? 0)}';
|
||||
}
|
||||
var changeLogFn = app != null ? getChangeLogFn(context, app.app) : null;
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@ -121,13 +127,26 @@ class _AppPageState extends State<AppPage> {
|
||||
.textTheme
|
||||
.bodyLarge!
|
||||
.copyWith(fontWeight: FontWeight.bold)),
|
||||
changeLogFn != null || app?.app.releaseDate != null
|
||||
? GestureDetector(
|
||||
onTap: changeLogFn,
|
||||
child: Text(
|
||||
app?.app.releaseDate == null
|
||||
? const SizedBox.shrink()
|
||||
: Text(
|
||||
app!.app.releaseDate.toString(),
|
||||
? tr('changes')
|
||||
: app!.app.releaseDate.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
style:
|
||||
Theme.of(context).textTheme.labelSmall!.copyWith(
|
||||
decoration: changeLogFn != null
|
||||
? TextDecoration.underline
|
||||
: null,
|
||||
fontStyle: changeLogFn != null
|
||||
? FontStyle.italic
|
||||
: null,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
@ -357,6 +376,9 @@ class _AppPageState extends State<AppPage> {
|
||||
!areDownloadsRunning
|
||||
? () async {
|
||||
try {
|
||||
var successMessage = app?.app.installedVersion == null
|
||||
? tr('installed')
|
||||
: tr('appsUpdated');
|
||||
HapticFeedback.heavyImpact();
|
||||
var res = await appsProvider.downloadAndInstallLatestApps(
|
||||
app?.app.id != null ? [app!.app.id] : [],
|
||||
@ -364,7 +386,7 @@ class _AppPageState extends State<AppPage> {
|
||||
);
|
||||
if (res.isNotEmpty && !trackOnly) {
|
||||
// ignore: use_build_context_synchronously
|
||||
showMessage(tr('appsUpdated'), context);
|
||||
showMessage(successMessage, context);
|
||||
}
|
||||
if (res.isNotEmpty && mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -26,6 +26,92 @@ class AppsPage extends StatefulWidget {
|
||||
State<AppsPage> createState() => AppsPageState();
|
||||
}
|
||||
|
||||
showChangeLogDialog(BuildContext context, App app, String? changesUrl,
|
||||
AppSource appSource, String changeLog) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return GeneratedFormModal(
|
||||
title: tr('changes'),
|
||||
items: const [],
|
||||
message: app.latestVersion,
|
||||
additionalWidgets: [
|
||||
changesUrl != null
|
||||
? GestureDetector(
|
||||
child: Text(
|
||||
changesUrl,
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
fontStyle: FontStyle.italic),
|
||||
),
|
||||
onTap: () {
|
||||
launchUrlString(changesUrl,
|
||||
mode: LaunchMode.externalApplication);
|
||||
},
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
changesUrl != null
|
||||
? const SizedBox(
|
||||
height: 16,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
appSource.changeLogIfAnyIsMarkDown
|
||||
? SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height - 350,
|
||||
child: Markdown(
|
||||
data: changeLog,
|
||||
onTapLink: (text, href, title) {
|
||||
if (href != null) {
|
||||
launchUrlString(
|
||||
href.startsWith('http://') ||
|
||||
href.startsWith('https://')
|
||||
? href
|
||||
: '${Uri.parse(app.url).origin}/$href',
|
||||
mode: LaunchMode.externalApplication);
|
||||
}
|
||||
},
|
||||
extensionSet: md.ExtensionSet(
|
||||
md.ExtensionSet.gitHubFlavored.blockSyntaxes,
|
||||
[
|
||||
md.EmojiSyntax(),
|
||||
...md.ExtensionSet.gitHubFlavored.inlineSyntaxes
|
||||
],
|
||||
),
|
||||
))
|
||||
: Text(changeLog),
|
||||
],
|
||||
singleNullReturnButton: tr('ok'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getChangeLogFn(BuildContext context, App app) {
|
||||
AppSource appSource =
|
||||
SourceProvider().getSource(app.url, overrideSource: app.overrideSource);
|
||||
String? changesUrl = appSource.changeLogPageFromStandardUrl(app.url);
|
||||
String? changeLog = app.changeLog;
|
||||
if (changeLog?.split('\n').length == 1) {
|
||||
if (RegExp(
|
||||
'(http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?')
|
||||
.hasMatch(changeLog!)) {
|
||||
if (changesUrl == null) {
|
||||
changesUrl = changeLog;
|
||||
changeLog = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (changeLog == null && changesUrl == null)
|
||||
? null
|
||||
: () {
|
||||
if (changeLog != null) {
|
||||
showChangeLogDialog(context, app, changesUrl, appSource, changeLog);
|
||||
} else {
|
||||
launchUrlString(changesUrl!, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class AppsPageState extends State<AppsPage> {
|
||||
AppsFilter filter = AppsFilter();
|
||||
final AppsFilter neutralFilter = AppsFilter();
|
||||
@ -262,66 +348,6 @@ class AppsPageState extends State<AppsPage> {
|
||||
.where((a) => selectedAppIds.contains(a.id))
|
||||
.toSet();
|
||||
|
||||
showChangeLogDialog(
|
||||
String? changesUrl, AppSource appSource, String changeLog, int index) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return GeneratedFormModal(
|
||||
title: tr('changes'),
|
||||
items: const [],
|
||||
message: listedApps[index].app.latestVersion,
|
||||
additionalWidgets: [
|
||||
changesUrl != null
|
||||
? GestureDetector(
|
||||
child: Text(
|
||||
changesUrl,
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
fontStyle: FontStyle.italic),
|
||||
),
|
||||
onTap: () {
|
||||
launchUrlString(changesUrl,
|
||||
mode: LaunchMode.externalApplication);
|
||||
},
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
changesUrl != null
|
||||
? const SizedBox(
|
||||
height: 16,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
appSource.changeLogIfAnyIsMarkDown
|
||||
? SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height - 350,
|
||||
child: Markdown(
|
||||
data: changeLog,
|
||||
onTapLink: (text, href, title) {
|
||||
if (href != null) {
|
||||
launchUrlString(
|
||||
href.startsWith('http://') ||
|
||||
href.startsWith('https://')
|
||||
? href
|
||||
: '${Uri.parse(listedApps[index].app.url).origin}/$href',
|
||||
mode: LaunchMode.externalApplication);
|
||||
}
|
||||
},
|
||||
extensionSet: md.ExtensionSet(
|
||||
md.ExtensionSet.gitHubFlavored.blockSyntaxes,
|
||||
[
|
||||
md.EmojiSyntax(),
|
||||
...md.ExtensionSet.gitHubFlavored.inlineSyntaxes
|
||||
],
|
||||
),
|
||||
))
|
||||
: Text(changeLog),
|
||||
],
|
||||
singleNullReturnButton: tr('ok'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getLoadingWidgets() {
|
||||
return [
|
||||
if (listedApps.isEmpty)
|
||||
@ -351,35 +377,6 @@ class AppsPageState extends State<AppsPage> {
|
||||
];
|
||||
}
|
||||
|
||||
getChangeLogFn(int appIndex) {
|
||||
AppSource appSource = SourceProvider().getSource(
|
||||
listedApps[appIndex].app.url,
|
||||
overrideSource: listedApps[appIndex].app.overrideSource);
|
||||
String? changesUrl =
|
||||
appSource.changeLogPageFromStandardUrl(listedApps[appIndex].app.url);
|
||||
String? changeLog = listedApps[appIndex].app.changeLog;
|
||||
if (changeLog?.split('\n').length == 1) {
|
||||
if (RegExp(
|
||||
'(http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?')
|
||||
.hasMatch(changeLog!)) {
|
||||
if (changesUrl == null) {
|
||||
changesUrl = changeLog;
|
||||
changeLog = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (changeLog == null && changesUrl == null)
|
||||
? null
|
||||
: () {
|
||||
if (changeLog != null) {
|
||||
showChangeLogDialog(changesUrl, appSource, changeLog, appIndex);
|
||||
} else {
|
||||
launchUrlString(changesUrl!,
|
||||
mode: LaunchMode.externalApplication);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getUpdateButton(int appIndex) {
|
||||
return IconButton(
|
||||
visualDensity: VisualDensity.compact,
|
||||
@ -444,7 +441,7 @@ class AppsPageState extends State<AppsPage> {
|
||||
}
|
||||
|
||||
getSingleAppHorizTile(int index) {
|
||||
var showChangesFn = getChangeLogFn(index);
|
||||
var showChangesFn = getChangeLogFn(context, listedApps[index].app);
|
||||
var hasUpdate = listedApps[index].app.installedVersion != null &&
|
||||
listedApps[index].app.installedVersion !=
|
||||
listedApps[index].app.latestVersion;
|
||||
|
@ -213,7 +213,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
||||
setState(() {
|
||||
importInProgress = true;
|
||||
});
|
||||
if (values['url'] != source.hosts[0]) {
|
||||
if (source.hosts.isEmpty || values['url'] != source.hosts[0]) {
|
||||
source = sourceProvider.getSource(values['url'],
|
||||
overrideSource: source.runtimeType.toString());
|
||||
}
|
||||
|
@ -328,6 +328,22 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
],
|
||||
),
|
||||
height16,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child:
|
||||
Text(tr('removeOnExternalUninstall'))),
|
||||
Switch(
|
||||
value: settingsProvider
|
||||
.removeOnExternalUninstall,
|
||||
onChanged: (value) {
|
||||
settingsProvider
|
||||
.removeOnExternalUninstall = value;
|
||||
})
|
||||
],
|
||||
),
|
||||
height16,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@ -341,6 +357,43 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
],
|
||||
),
|
||||
height16,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(tr(
|
||||
'beforeNewInstallsShareToAppVerifier')),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
launchUrlString(
|
||||
'https://github.com/soupslurpr/AppVerifier',
|
||||
mode: LaunchMode
|
||||
.externalApplication);
|
||||
},
|
||||
child: Text(
|
||||
tr('about'),
|
||||
style: const TextStyle(
|
||||
decoration:
|
||||
TextDecoration.underline,
|
||||
fontSize: 12),
|
||||
)),
|
||||
],
|
||||
)),
|
||||
Switch(
|
||||
value: settingsProvider
|
||||
.beforeNewInstallsShareToAppVerifier,
|
||||
onChanged: (value) {
|
||||
settingsProvider
|
||||
.beforeNewInstallsShareToAppVerifier =
|
||||
value;
|
||||
})
|
||||
],
|
||||
),
|
||||
height16,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@ -473,22 +526,6 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
],
|
||||
),
|
||||
height16,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child:
|
||||
Text(tr('removeOnExternalUninstall'))),
|
||||
Switch(
|
||||
value: settingsProvider
|
||||
.removeOnExternalUninstall,
|
||||
onChanged: (value) {
|
||||
settingsProvider
|
||||
.removeOnExternalUninstall = value;
|
||||
})
|
||||
],
|
||||
),
|
||||
height16,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
@ -5,6 +5,7 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
@ -31,6 +32,7 @@ import 'package:obtainium/providers/source_provider.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:android_intent_plus/android_intent.dart';
|
||||
import 'package:flutter_archive/flutter_archive.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:shared_storage/shared_storage.dart' as saf;
|
||||
import 'native_provider.dart';
|
||||
|
||||
@ -202,14 +204,18 @@ Future<String> checkPartialDownloadHash(String url, int bytesToGrab,
|
||||
Future<File> downloadFile(
|
||||
String url, String fileNameNoExt, Function? onProgress, String destDir,
|
||||
{bool useExisting = true, Map<String, String>? headers}) async {
|
||||
// Send the initial request but cancel it as soon as you have the headers
|
||||
var reqHeaders = headers ?? {};
|
||||
var req = Request('GET', Uri.parse(url));
|
||||
if (headers != null) {
|
||||
req.headers.addAll(headers);
|
||||
}
|
||||
req.headers.addAll(reqHeaders);
|
||||
var client = http.Client();
|
||||
StreamedResponse response = await client.send(req);
|
||||
String ext =
|
||||
response.headers['content-disposition']?.split('.').last ?? 'apk';
|
||||
var resHeaders = response.headers;
|
||||
|
||||
// Use the headers to decide what the file extension is, and
|
||||
// whether it supports partial downloads (range request), and
|
||||
// what the total size of the file is (if provided)
|
||||
String ext = resHeaders['content-disposition']?.split('.').last ?? 'apk';
|
||||
if (ext.endsWith('"') || ext.endsWith("other")) {
|
||||
ext = ext.substring(0, ext.length - 1);
|
||||
}
|
||||
@ -217,41 +223,108 @@ Future<File> downloadFile(
|
||||
ext = 'apk';
|
||||
}
|
||||
File downloadedFile = File('$destDir/$fileNameNoExt.$ext');
|
||||
if (!(downloadedFile.existsSync() && useExisting)) {
|
||||
|
||||
bool rangeFeatureEnabled = false;
|
||||
if (resHeaders['accept-ranges']?.isNotEmpty == true) {
|
||||
rangeFeatureEnabled =
|
||||
resHeaders['accept-ranges']?.trim().toLowerCase() == 'bytes';
|
||||
}
|
||||
|
||||
// If you have an existing file that is usable,
|
||||
// decide whether you can use it (either return full or resume partial)
|
||||
var fullContentLength = response.contentLength;
|
||||
if (useExisting && downloadedFile.existsSync()) {
|
||||
var length = downloadedFile.lengthSync();
|
||||
if (fullContentLength == null) {
|
||||
// Assume full
|
||||
client.close();
|
||||
return downloadedFile;
|
||||
} else {
|
||||
// Check if resume needed/possible
|
||||
if (length == fullContentLength) {
|
||||
client.close();
|
||||
return downloadedFile;
|
||||
}
|
||||
if (length > fullContentLength) {
|
||||
useExisting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Download to a '.temp' file (to distinguish btn. complete/incomplete files)
|
||||
File tempDownloadedFile = File('${downloadedFile.path}.part');
|
||||
if (tempDownloadedFile.existsSync()) {
|
||||
|
||||
// If the range feature is not available (or you need to start a ranged req from 0),
|
||||
// complete the already-started request, else cancel it and start a ranged request,
|
||||
// and open the file for writing in the appropriate mode
|
||||
var targetFileLength = useExisting && tempDownloadedFile.existsSync()
|
||||
? tempDownloadedFile.lengthSync()
|
||||
: null;
|
||||
int rangeStart = targetFileLength ?? 0;
|
||||
IOSink? sink;
|
||||
if (rangeFeatureEnabled && fullContentLength != null && rangeStart > 0) {
|
||||
client.close();
|
||||
client = http.Client();
|
||||
req = Request('GET', Uri.parse(url));
|
||||
req.headers.addAll(reqHeaders);
|
||||
req.headers.addAll({'range': 'bytes=$rangeStart-${fullContentLength - 1}'});
|
||||
response = await client.send(req);
|
||||
sink = tempDownloadedFile.openWrite(mode: FileMode.writeOnlyAppend);
|
||||
} else if (tempDownloadedFile.existsSync()) {
|
||||
tempDownloadedFile.deleteSync(recursive: true);
|
||||
}
|
||||
var length = response.contentLength;
|
||||
sink ??= tempDownloadedFile.openWrite(mode: FileMode.writeOnly);
|
||||
|
||||
// Perform the download
|
||||
var received = 0;
|
||||
double? progress;
|
||||
var sink = tempDownloadedFile.openWrite();
|
||||
if (rangeStart > 0 && fullContentLength != null) {
|
||||
received = rangeStart;
|
||||
}
|
||||
await response.stream.map((s) {
|
||||
received += s.length;
|
||||
progress = (length != null ? received / length * 100 : 30);
|
||||
progress =
|
||||
(fullContentLength != null ? (received / fullContentLength) * 100 : 30);
|
||||
if (onProgress != null) {
|
||||
onProgress(progress);
|
||||
}
|
||||
return s;
|
||||
}).pipe(sink);
|
||||
await sink.close();
|
||||
bool likelyCorruptFile = (progress ?? 0) > 101;
|
||||
progress = null;
|
||||
if (onProgress != null) {
|
||||
onProgress(progress);
|
||||
}
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode < 200 ||
|
||||
response.statusCode > 299 ||
|
||||
likelyCorruptFile) {
|
||||
tempDownloadedFile.deleteSync(recursive: true);
|
||||
throw response.reasonPhrase ?? tr('unexpectedError');
|
||||
}
|
||||
if (tempDownloadedFile.existsSync()) {
|
||||
tempDownloadedFile.renameSync(downloadedFile.path);
|
||||
}
|
||||
} else {
|
||||
client.close();
|
||||
}
|
||||
return downloadedFile;
|
||||
}
|
||||
|
||||
Future<Map<String, String>> getHeaders(String url,
|
||||
{Map<String, String>? headers}) async {
|
||||
var req = http.Request('GET', Uri.parse(url));
|
||||
if (headers != null) {
|
||||
req.headers.addAll(headers);
|
||||
}
|
||||
var client = http.Client();
|
||||
var response = await client.send(req);
|
||||
if (response.statusCode < 200 || response.statusCode > 299) {
|
||||
throw ObtainiumError(response.reasonPhrase ?? tr('unexpectedError'));
|
||||
}
|
||||
var returnHeaders = response.headers;
|
||||
client.close();
|
||||
return returnHeaders;
|
||||
}
|
||||
|
||||
Future<PackageInfo?> getInstalledInfo(String? packageName,
|
||||
{bool printErr = true}) async {
|
||||
if (packageName != null) {
|
||||
@ -493,13 +566,14 @@ class AppsProvider with ChangeNotifier {
|
||||
zipFile: File(filePath), destinationDir: Directory(destinationPath));
|
||||
}
|
||||
|
||||
Future<void> installXApkDir(DownloadedXApkDir dir,
|
||||
Future<bool> installXApkDir(
|
||||
DownloadedXApkDir dir, BuildContext? firstTimeWithContext,
|
||||
{bool needsBGWorkaround = false}) async {
|
||||
// We don't know which APKs in an XAPK are supported by the user's device
|
||||
// So we try installing all of them and assume success if at least one installed
|
||||
// If 0 APKs installed, throw the first install error encountered
|
||||
try {
|
||||
var somethingInstalled = false;
|
||||
try {
|
||||
MultiAppMultiError errors = MultiAppMultiError();
|
||||
for (var file in dir.extracted
|
||||
.listSync(recursive: true, followLinks: false)
|
||||
@ -507,7 +581,8 @@ class AppsProvider with ChangeNotifier {
|
||||
if (file.path.toLowerCase().endsWith('.apk')) {
|
||||
try {
|
||||
somethingInstalled = somethingInstalled ||
|
||||
await installApk(DownloadedApk(dir.appId, file),
|
||||
await installApk(
|
||||
DownloadedApk(dir.appId, file), firstTimeWithContext,
|
||||
needsBGWorkaround: needsBGWorkaround);
|
||||
} catch (e) {
|
||||
logs.add(
|
||||
@ -526,10 +601,22 @@ class AppsProvider with ChangeNotifier {
|
||||
} finally {
|
||||
dir.extracted.delete(recursive: true);
|
||||
}
|
||||
return somethingInstalled;
|
||||
}
|
||||
|
||||
Future<bool> installApk(DownloadedApk file,
|
||||
Future<bool> installApk(
|
||||
DownloadedApk file, BuildContext? firstTimeWithContext,
|
||||
{bool needsBGWorkaround = false}) async {
|
||||
if (firstTimeWithContext != null &&
|
||||
settingsProvider.beforeNewInstallsShareToAppVerifier &&
|
||||
(await getInstalledInfo('dev.soupslurpr.appverifier')) != null) {
|
||||
XFile f = XFile.fromData(file.file.readAsBytesSync(),
|
||||
mimeType: 'application/vnd.android.package-archive');
|
||||
Fluttertoast.showToast(
|
||||
msg: tr('appVerifierInstructionToast'),
|
||||
toastLength: Toast.LENGTH_LONG);
|
||||
await Share.shareXFiles([f]);
|
||||
}
|
||||
var newInfo =
|
||||
await pm.getPackageArchiveInfo(archiveFilePath: file.file.path);
|
||||
if (newInfo == null) {
|
||||
@ -570,7 +657,13 @@ class AppsProvider with ChangeNotifier {
|
||||
}
|
||||
bool installed = false;
|
||||
if (code != null && code != 0 && code != 3) {
|
||||
try {
|
||||
file.file.deleteSync(recursive: true);
|
||||
} catch (e) {
|
||||
//
|
||||
} finally {
|
||||
throw InstallError(code);
|
||||
}
|
||||
} else if (code == 0) {
|
||||
installed = true;
|
||||
apps[file.appId]!.app.installedVersion =
|
||||
@ -710,7 +803,7 @@ class AppsProvider with ChangeNotifier {
|
||||
appsToInstall =
|
||||
moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId);
|
||||
|
||||
Future<void> updateFn(String id, {bool skipInstalls = false}) async {
|
||||
Future<String> updateFn(String id, {bool skipInstalls = false}) async {
|
||||
try {
|
||||
var downloadedArtifact =
|
||||
// ignore: use_build_context_synchronously
|
||||
@ -723,8 +816,8 @@ class AppsProvider with ChangeNotifier {
|
||||
} else {
|
||||
downloadedDir = downloadedArtifact as DownloadedXApkDir;
|
||||
}
|
||||
var appId = downloadedFile?.appId ?? downloadedDir!.appId;
|
||||
bool willBeSilent = await canInstallSilently(apps[appId]!.app);
|
||||
var id = downloadedFile?.appId ?? downloadedDir!.appId;
|
||||
bool willBeSilent = await canInstallSilently(apps[id]!.app);
|
||||
if (!settingsProvider.useShizuku) {
|
||||
if (!(await settingsProvider.getInstallPermission(enforce: false))) {
|
||||
throw ObtainiumError(tr('cancelled'));
|
||||
@ -745,33 +838,43 @@ class AppsProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
try {
|
||||
if (!skipInstalls) {
|
||||
bool sayInstalled = true;
|
||||
var contextIfNewInstall =
|
||||
apps[id]?.installedInfo == null ? context : null;
|
||||
if (downloadedFile != null) {
|
||||
if (willBeSilent && context == null) {
|
||||
installApk(downloadedFile, needsBGWorkaround: true);
|
||||
installApk(downloadedFile, contextIfNewInstall,
|
||||
needsBGWorkaround: true);
|
||||
} else {
|
||||
await installApk(downloadedFile);
|
||||
sayInstalled =
|
||||
await installApk(downloadedFile, contextIfNewInstall);
|
||||
}
|
||||
} else {
|
||||
if (willBeSilent && context == null) {
|
||||
installXApkDir(downloadedDir!, needsBGWorkaround: true);
|
||||
installXApkDir(downloadedDir!, contextIfNewInstall,
|
||||
needsBGWorkaround: true);
|
||||
} else {
|
||||
await installXApkDir(downloadedDir!);
|
||||
sayInstalled =
|
||||
await installXApkDir(downloadedDir!, contextIfNewInstall);
|
||||
}
|
||||
}
|
||||
if (willBeSilent && context == null) {
|
||||
notificationsProvider?.notify(SilentUpdateAttemptNotification(
|
||||
[apps[appId]!.app],
|
||||
id: appId.hashCode));
|
||||
[apps[id]!.app],
|
||||
id: id.hashCode));
|
||||
}
|
||||
if (sayInstalled) {
|
||||
installedIds.add(id);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
apps[id]?.downloadProgress = null;
|
||||
notifyListeners();
|
||||
}
|
||||
installedIds.add(id);
|
||||
} catch (e) {
|
||||
errors.add(id, e, appName: apps[id]?.name);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
if (forceParallelDownloads || !settingsProvider.parallelDownloads) {
|
||||
@ -779,9 +882,9 @@ class AppsProvider with ChangeNotifier {
|
||||
await updateFn(id);
|
||||
}
|
||||
} else {
|
||||
await Future.wait(
|
||||
List<String> ids = await Future.wait(
|
||||
appsToInstall.map((id) => updateFn(id, skipInstalls: true)));
|
||||
for (var id in appsToInstall) {
|
||||
for (var id in ids) {
|
||||
if (!errors.appIdNames.containsKey(id)) {
|
||||
await updateFn(id);
|
||||
}
|
||||
|
@ -28,8 +28,22 @@ enum SortOrderSettings { ascending, descending }
|
||||
|
||||
const maxAPIRateLimitMinutes = 30;
|
||||
const minUpdateIntervalMinutes = maxAPIRateLimitMinutes + 30;
|
||||
const maxUpdateIntervalMinutes = 4320;
|
||||
List<int> updateIntervals = [15, 30, 60, 120, 180, 360, 720, 1440, 4320, 0]
|
||||
const maxUpdateIntervalMinutes = 43200;
|
||||
List<int> updateIntervals = [
|
||||
15,
|
||||
30,
|
||||
60,
|
||||
120,
|
||||
180,
|
||||
360,
|
||||
720,
|
||||
1440,
|
||||
4320,
|
||||
10080,
|
||||
20160,
|
||||
43200,
|
||||
0
|
||||
]
|
||||
.where((element) =>
|
||||
(element >= minUpdateIntervalMinutes &&
|
||||
element <= maxUpdateIntervalMinutes) ||
|
||||
@ -462,4 +476,13 @@ class SettingsProvider with ChangeNotifier {
|
||||
prefs?.setStringList('searchDeselected', list);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get beforeNewInstallsShareToAppVerifier {
|
||||
return prefs?.getBool('beforeNewInstallsShareToAppVerifier') ?? true;
|
||||
}
|
||||
|
||||
set beforeNewInstallsShareToAppVerifier(bool val) {
|
||||
prefs?.setBool('beforeNewInstallsShareToAppVerifier', val);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
@ -736,7 +736,6 @@ class SourceProvider {
|
||||
FDroid(),
|
||||
FDroidRepo(),
|
||||
IzzyOnDroid(),
|
||||
SourceForge(),
|
||||
SourceHut(),
|
||||
APKPure(),
|
||||
Aptoide(),
|
||||
@ -819,7 +818,7 @@ class SourceProvider {
|
||||
AppSource source, String url, Map<String, dynamic> additionalSettings,
|
||||
{App? currentApp,
|
||||
bool trackOnlyOverride = false,
|
||||
String? overrideSource,
|
||||
bool sourceIsOverriden = false,
|
||||
bool inferAppIdIfOptional = false}) async {
|
||||
if (trackOnlyOverride || source.enforceTrackOnly) {
|
||||
additionalSettings['trackOnly'] = true;
|
||||
@ -887,7 +886,9 @@ class SourceProvider {
|
||||
categories: currentApp?.categories ?? const [],
|
||||
releaseDate: apk.releaseDate,
|
||||
changeLog: apk.changeLog,
|
||||
overrideSource: overrideSource ?? currentApp?.overrideSource,
|
||||
overrideSource: sourceIsOverriden
|
||||
? source.runtimeType.toString()
|
||||
: currentApp?.overrideSource,
|
||||
allowIdChange: currentApp?.allowIdChange ??
|
||||
trackOnly ||
|
||||
(source.appIdInferIsOptional &&
|
||||
@ -911,6 +912,7 @@ class SourceProvider {
|
||||
apps.add(await getApp(
|
||||
source,
|
||||
url,
|
||||
sourceIsOverriden: sourceOverride != null,
|
||||
getDefaultValuesFromFormItems(
|
||||
source.combinedAppSpecificSettingFormItems)));
|
||||
} catch (e) {
|
||||
|
208
pubspec.lock
208
pubspec.lock
@ -5,10 +5,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: android_intent_plus
|
||||
sha256: e1c62bb41c90e15083b7fb84dc327fe90396cc9c1445b55ff1082144fabfb4d9
|
||||
sha256: e92d14009f3f6ebafca6a601958aaebb793559fb03a1961fe3c5596db95af2cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.3"
|
||||
version: "5.0.1"
|
||||
android_package_installer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -22,10 +22,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: android_package_manager
|
||||
sha256: e52ca607b9f19f95d5dae4211ed8fa93e67093f22ac570db47489c5bca512940
|
||||
sha256: "2de859fae7226a7de1c1ff9a2308f1967599408800330501a1ce97927c051153"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
version: "0.7.1"
|
||||
animations:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -38,10 +38,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: app_links
|
||||
sha256: "4e392b5eba997df356ca6021f28431ce1cfeb16758699553a94b13add874a3bb"
|
||||
sha256: "42dc15aecf2618ace4ffb74a2e58a50e45cd1b9f2c17c8f0cafe4c297f08c815"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.5.0"
|
||||
version: "4.0.1"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -70,10 +70,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: background_fetch
|
||||
sha256: "34550cf9b383e5a1844e7d22119aa500508c7df9421fa967c9fb4430d6cb2878"
|
||||
sha256: dbffec0317ccdef6e2014cb543e147f52441e29c4fcb53dfd23558c4d92ddece
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
version: "1.3.2"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -126,18 +126,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: connectivity_plus
|
||||
sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0"
|
||||
sha256: e9feae83b1849f61bad9f6f33ee00646e3410d54ce0821e02f262f9901dad3c9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
version: "6.0.1"
|
||||
connectivity_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: connectivity_plus_platform_interface
|
||||
sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a
|
||||
sha256: b6a56efe1e6675be240de39107281d4034b64ac23438026355b4234042a35adb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.4"
|
||||
version: "2.0.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -150,10 +150,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
|
||||
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3+8"
|
||||
version: "0.3.4+1"
|
||||
crypto:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -190,10 +190,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110"
|
||||
sha256: "50fb435ed30c6d2525cbfaaa0f46851ea6131315f213c0d921b0e407b34e3b84"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.1.2"
|
||||
version: "10.0.1"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -206,18 +206,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dynamic_color
|
||||
sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b
|
||||
sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.9"
|
||||
version: "1.7.0"
|
||||
easy_localization:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: easy_localization
|
||||
sha256: de63e3b422adfc97f256cbb3f8cf12739b6a4993d390f3cadb3f51837afaefe5
|
||||
sha256: c145aeb6584aedc7c862ab8c737c3277788f47488bfdf9bae0fe112bd0a4789c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
version: "3.0.5"
|
||||
easy_logger:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -238,10 +238,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.1.2"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -254,10 +254,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: "4e42aacde3b993c5947467ab640882c56947d9d27342a5b6f2895b23956954a6"
|
||||
sha256: d1d0ac3966b36dc3e66eeefb40280c17feb87fa2099c6e22e6a1fc959327bd03
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "8.0.0+1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -299,18 +299,18 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
|
||||
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
flutter_local_notifications:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_local_notifications
|
||||
sha256: c18f1de98fe0bb9dd5ba91e1330d4febc8b6a7de6aae3ffe475ef423723e72f3
|
||||
sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "16.3.2"
|
||||
version: "17.0.0"
|
||||
flutter_local_notifications_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -336,10 +336,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: "30088ce826b5b9cfbf9e8bece34c716c8a59fa54461dcae1e4ac01a94639e762"
|
||||
sha256: "87e11b9df25a42e2db315b8b7a51fae8e66f57a4b2f50ec4b822d0fa155e6b52"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.18+3"
|
||||
version: "0.6.22"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -394,10 +394,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
|
||||
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.2.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -410,10 +410,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "49a0d4b0c12402853d3f227fe7c315601b238d126aa4caa5dbb2dcf99421aa4a"
|
||||
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.6"
|
||||
version: "4.1.7"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -426,10 +426,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
version: "0.7.1"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -438,6 +438,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -450,34 +474,34 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: markdown
|
||||
sha256: "1b134d9f8ff2da15cb298efe6cd8b7d2a78958c1b00384ebcbdf13fe340a6c90"
|
||||
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.1"
|
||||
version: "7.2.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
version: "1.11.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -506,10 +530,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -562,26 +586,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "45ff3fbcb99040fde55c528d5e3e6ca29171298a85436274d49c6201002087d6"
|
||||
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.2.0"
|
||||
version: "11.3.1"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: "758284a0976772f9c744d6384fc5dc4834aa61e3f7aa40492927f244767374eb"
|
||||
sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.3"
|
||||
version: "12.0.5"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: c6bf440f80acd2a873d3d91a699e4cc770f86e7e6b576dda98759e8b92b39830
|
||||
sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.3.0"
|
||||
version: "9.4.4"
|
||||
permission_handler_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -594,10 +618,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "5c43148f2bfb6d14c5a8162c0a712afe891f2d847f35fcff29c406b37da43c3c"
|
||||
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
version: "4.2.1"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -642,26 +666,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "6.1.2"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: share_plus
|
||||
sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900"
|
||||
sha256: "05ec043470319bfbabe0adbc90d3a84cbff0426b9d9f3a6e2ad3e131fa5fa629"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.2"
|
||||
version: "8.0.2"
|
||||
share_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share_plus_platform_interface
|
||||
sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956
|
||||
sha256: "251eb156a8b5fa9ce033747d73535bf53911071f8d3b6f4f0b578505ce0d4496"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
version: "3.4.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -706,10 +730,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
|
||||
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
version: "2.3.0"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -722,10 +746,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_storage
|
||||
sha256: "7c65a9d64f0f5521256be974cfd74010af12196657cec9f9fb7b03b2f11bcaf6"
|
||||
sha256: cf20428d06af065311b71e09cbfbbfe431e979a3bf9180001c1952129b7c708f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
version: "0.8.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@ -759,10 +783,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5"
|
||||
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
version: "2.5.4"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -831,26 +855,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c
|
||||
sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.4"
|
||||
version: "6.2.5"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f"
|
||||
sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.2"
|
||||
version: "6.3.0"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
|
||||
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.4"
|
||||
version: "6.2.5"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -871,18 +895,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f
|
||||
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.3.2"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b
|
||||
sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
version: "2.3.0"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -907,30 +931,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
version: "0.5.1"
|
||||
webview_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: d81b68e88cc353e546afb93fb38958e3717282c5ac6e5d3be4a4aef9fc3c1413
|
||||
sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.0"
|
||||
version: "4.7.0"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: "4ea3c4e1b8ed590162b15b8a61b41b1ef3ff179a314627c16ce40c086d94b8af"
|
||||
sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.14.0"
|
||||
version: "3.16.0"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -943,18 +975,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: b99ca8d8bae9c6b43d568218691aa537fb0aeae1d7d34eadf112a6aa36d26506
|
||||
sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.11.0"
|
||||
version: "3.13.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||
sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
version: "5.3.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -988,5 +1020,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.2.3 <4.0.0"
|
||||
flutter: ">=3.16.6"
|
||||
dart: ">=3.3.0 <4.0.0"
|
||||
flutter: ">=3.19.0"
|
||||
|
16
pubspec.yaml
16
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: 1.0.2+252 # When changing this, update the tag in main() accordingly
|
||||
version: 1.1.0+2257
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
@ -38,7 +38,7 @@ dependencies:
|
||||
cupertino_icons: ^1.0.5
|
||||
path_provider: ^2.0.11
|
||||
flutter_fgbg: ^0.3.0 # Try removing reliance on this
|
||||
flutter_local_notifications: ^16.1.0
|
||||
flutter_local_notifications: ^17.0.0
|
||||
provider: ^6.0.3
|
||||
http: ^1.0.0
|
||||
webview_flutter: ^4.0.0
|
||||
@ -48,25 +48,25 @@ dependencies:
|
||||
url_launcher: ^6.1.5
|
||||
permission_handler: ^11.0.0
|
||||
fluttertoast: ^8.0.9
|
||||
device_info_plus: ^9.0.0
|
||||
file_picker: ^6.0.0
|
||||
device_info_plus: ^10.0.1
|
||||
file_picker: ^8.0.0+1
|
||||
animations: ^2.0.4
|
||||
android_package_installer:
|
||||
git:
|
||||
url: https://github.com/ImranR98/android_package_installer
|
||||
ref: main
|
||||
android_package_manager: ^0.7.0
|
||||
share_plus: ^7.0.0
|
||||
share_plus: ^8.0.2
|
||||
sqflite: ^2.2.0+3
|
||||
easy_localization: ^3.0.1
|
||||
android_intent_plus: ^4.0.0
|
||||
android_intent_plus: ^5.0.1
|
||||
flutter_markdown: ^0.6.14
|
||||
flutter_archive: ^6.0.0
|
||||
hsluv: ^1.1.3
|
||||
connectivity_plus: ^5.0.0
|
||||
connectivity_plus: ^6.0.1
|
||||
shared_storage: ^0.8.0
|
||||
crypto: ^3.0.3
|
||||
app_links: ^3.5.0
|
||||
app_links: ^4.0.0
|
||||
background_fetch: ^1.2.1
|
||||
|
||||
dev_dependencies:
|
||||
|
Reference in New Issue
Block a user