mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-13 13:26:43 +02:00
Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
f3d2dfe386 | |||
677790af9c | |||
85941349d8 | |||
db755e0f90 | |||
1019a01249 | |||
35da44bc64 | |||
b7ce07f965 | |||
f2c15c5c8e | |||
8f0a6b7635 | |||
491c42d68b | |||
a883857230 | |||
6468d0edcc | |||
48eeeb4f00 | |||
0b25f25669 | |||
d76b7375cb | |||
f76aa51b54 | |||
9e19a293d4 | |||
381758550c | |||
da496f7c53 | |||
f905aa7dfd | |||
0db27c773b | |||
4e968826ba | |||
e95f575530 | |||
3943caeedb | |||
4495051813 | |||
558d23b8c0 | |||
2e90d1fb18 |
2
.flutter
2
.flutter
Submodule .flutter updated: ba39319843...300451adae
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@ -15,7 +15,16 @@ 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
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
@ -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/)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -299,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?"
|
||||
|
@ -218,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",
|
||||
"gitlabPATLabel": "Osobní přístupový token GitLab",
|
||||
"about": "O",
|
||||
"requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)",
|
||||
"checkOnStart": "Zkontrolovat jednou při spuštění",
|
||||
@ -299,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?"
|
||||
|
@ -299,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?"
|
||||
|
@ -299,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": "Remove App?",
|
||||
"other": "Remove Apps?"
|
||||
|
@ -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,75 +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",
|
||||
"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",
|
||||
@ -110,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",
|
||||
@ -122,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.",
|
||||
@ -145,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)",
|
||||
@ -173,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",
|
||||
@ -192,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.",
|
||||
@ -212,8 +212,8 @@
|
||||
"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",
|
||||
@ -262,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",
|
||||
@ -297,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",
|
||||
@ -336,16 +338,16 @@
|
||||
"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.",
|
||||
|
@ -299,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": "برنامه ها حذف شوند؟"
|
||||
|
@ -299,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 ?"
|
||||
|
@ -218,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",
|
||||
"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",
|
||||
@ -299,6 +299,8 @@
|
||||
"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ásokat?"
|
||||
|
@ -218,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",
|
||||
"gitlabPATLabel": "GitLab Token di accesso personale",
|
||||
"about": "Informazioni",
|
||||
"requiresCredentialsInSettings": "{}: Servono credenziali aggiuntive (in Impostazioni)",
|
||||
"checkOnStart": "Controlla una volta all'avvio",
|
||||
@ -299,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?"
|
||||
|
@ -299,6 +299,8 @@
|
||||
"note": "注",
|
||||
"selfHostedNote": "ドロップダウン\"{}\"を使用すると、あらゆるソースのセルフホスト/カスタムインスタンスにアクセスできます。",
|
||||
"badDownload": "APK を解析できませんでした(互換性がないか、部分的にダウンロードされています)。",
|
||||
"beforeNewInstallsShareToAppVerifier": "AppVerifierで新しいアプリを共有する(利用可能な場合)",
|
||||
"appVerifierInstructionToast": "AppVerifierに共有し、準備ができたらここに戻ってください。",
|
||||
"removeAppQuestion": {
|
||||
"one": "アプリを削除しますか?",
|
||||
"other": "アプリを削除しますか?"
|
||||
|
@ -218,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",
|
||||
"gitlabPATLabel": "GitLab persoonlijk toegangskenmerk",
|
||||
"about": "Over",
|
||||
"requiresCredentialsInSettings": "{}: Dit vereist aanvullende referenties (in Instellingen)",
|
||||
"checkOnStart": "Controleren op updates bij opstarten",
|
||||
@ -299,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?"
|
||||
|
@ -299,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?",
|
||||
|
@ -299,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?"
|
||||
|
@ -299,6 +299,8 @@
|
||||
"note": "Примечание",
|
||||
"selfHostedNote": "Выпадающий список \"{}\" можно использовать для доступа к самостоятельно размещенным/настроенным экземплярам любого источника.",
|
||||
"badDownload": "APK не удалось разобрать (несовместимая или неполная загрузка)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Поделитесь новыми приложениями с AppVerifier (если доступно)",
|
||||
"appVerifierInstructionToast": "Поделитесь с AppVerifier, а затем вернитесь сюда, когда будете готовы.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Удалить приложение?",
|
||||
"other": "Удалить приложения?"
|
||||
|
@ -218,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",
|
||||
"gitlabPATLabel": "Personligt åtkomsttoken för GitLab",
|
||||
"about": "Om",
|
||||
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
|
||||
"checkOnStart": "Kolla efter uppdateringar vid start",
|
||||
@ -299,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?"
|
||||
|
@ -299,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?"
|
||||
|
@ -28,13 +28,13 @@
|
||||
"xIsTrackOnly": "{} - тільки відстежування",
|
||||
"source": "Джерело",
|
||||
"app": "застосунок",
|
||||
"appsFromSourceAreTrackOnly": "Додатки з цього джерела є лише для відстежування.",
|
||||
"appsFromSourceAreTrackOnly": "Застосунки з цього джерела є лише для відстежування.",
|
||||
"youPickedTrackOnly": "Ви вибрали опцію лише для відстежування.",
|
||||
"trackOnlyAppDescription": "Застосунок буде відстежуватися для оновлень, але Obtainium не зможе його завантажити або встановити.",
|
||||
"cancelled": "Скасовано",
|
||||
"appAlreadyAdded": "Застосунок вже додано",
|
||||
"alreadyUpToDateQuestion": "Застосунок вже оновлено?",
|
||||
"addApp": "Додати Застосунок",
|
||||
"addApp": "Додати",
|
||||
"appSourceURL": "URL-адреса джерела застосунку",
|
||||
"error": "Помилка",
|
||||
"add": "Додати",
|
||||
@ -44,10 +44,10 @@
|
||||
"supportedSources": "Підтримувані джерела",
|
||||
"trackOnlyInBrackets": "(Тільки для відстеження)",
|
||||
"searchableInBrackets": "(Можливий пошук)",
|
||||
"appsString": "Додатки",
|
||||
"noApps": "Додатків немає",
|
||||
"noAppsForFilter": "Додатків для фільтрації немає",
|
||||
"byX": "За {}",
|
||||
"appsString": "Застосунки",
|
||||
"noApps": "Застосунків немає",
|
||||
"noAppsForFilter": "Застосунків для фільтрації немає",
|
||||
"byX": "Від {}",
|
||||
"percentProgress": "Прогрес: {}%",
|
||||
"pleaseWait": "Будь ласка, зачекайте",
|
||||
"updateAvailable": "Доступно оновлення",
|
||||
@ -56,35 +56,35 @@
|
||||
"selectAll": "Вибрати все",
|
||||
"deselectX": "Скасувати вибір {}",
|
||||
"xWillBeRemovedButRemainInstalled": "{} буде видалено з Obtainium, але залишиться встановленим на пристрої.",
|
||||
"removeSelectedAppsQuestion": "Видалити вибрані додатки?",
|
||||
"removeSelectedApps": "Видалити вибрані додатки",
|
||||
"removeSelectedAppsQuestion": "Видалити вибрані застосунки?",
|
||||
"removeSelectedApps": "Видалити вибрані застосунки",
|
||||
"updateX": "Оновити {}",
|
||||
"installX": "Встановити {}",
|
||||
"markXTrackOnlyAsUpdated": "Позначити {}\n(Тільки відстежування)\nяк оновлено",
|
||||
"changeX": "Змінити {}",
|
||||
"installUpdateApps": "Встановити/Оновити додатки",
|
||||
"installUpdateSelectedApps": "Встановити/Оновити вибрані додатки",
|
||||
"markXSelectedAppsAsUpdated": "Позначити {} вибрані додатки як оновлені?",
|
||||
"installUpdateApps": "Встановити/Оновити застосунки",
|
||||
"installUpdateSelectedApps": "Встановити/Оновити вибрані застосунки",
|
||||
"markXSelectedAppsAsUpdated": "Позначити {} вибрані застосунки як оновлені?",
|
||||
"no": "Ні",
|
||||
"yes": "Так",
|
||||
"markSelectedAppsUpdated": "Позначити вибрані додатки як оновлені",
|
||||
"markSelectedAppsUpdated": "Позначити вибрані застосунки як оновлені",
|
||||
"pinToTop": "Закріпити угорі",
|
||||
"unpinFromTop": "Відкріпити зверху",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Скинути статус встановлення для вибраних додатків?",
|
||||
"installStatusOfXWillBeResetExplanation": "Статус встановлення будь-яких вибраних додатків буде скинутий.\n\nЦе може допомогти, коли версія застосунку, відображена в Obtainium, є неправильною через невдалі оновлення або інші проблеми.",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Скинути статус встановлення для вибраних застосунків?",
|
||||
"installStatusOfXWillBeResetExplanation": "Статус встановлення будь-яких вибраних застосунків буде скинутий.\n\nЦе може допомогти, коли версія застосунку, відображена в Obtainium, є неправильною через невдалі оновлення або інші проблеми.",
|
||||
"customLinkMessage": "Ці посилання працюють на пристроях з встановленим Obtainium",
|
||||
"shareAppConfigLinks": "Поділитися посиланнями на конфігурацію Застосунку як HTML",
|
||||
"shareSelectedAppURLs": "Поділитися вибраними URL-адресами додатків",
|
||||
"shareSelectedAppURLs": "Поділитися вибраними URL-адресами застосунків",
|
||||
"resetInstallStatus": "Скинути статус встановлення",
|
||||
"more": "Більше",
|
||||
"removeOutdatedFilter": "Видалити фільтр застарілих додатків",
|
||||
"showOutdatedOnly": "Показати лише застарілі додатки",
|
||||
"removeOutdatedFilter": "Видалити фільтр застарілих застосунків",
|
||||
"showOutdatedOnly": "Показати лише застарілі застосунки",
|
||||
"filter": "Фільтр",
|
||||
"filterApps": "Фільтрувати додатки",
|
||||
"filterApps": "Фільтрувати застосунки",
|
||||
"appName": "Назва застосунку",
|
||||
"author": "Автор",
|
||||
"upToDateApps": "Актуальні додатки",
|
||||
"nonInstalledApps": "Невстановлені додатки",
|
||||
"upToDateApps": "Актуальні застосунки",
|
||||
"nonInstalledApps": "Невстановлені застосунки",
|
||||
"importExport": "Імпорт/Експорт",
|
||||
"settings": "Налаштування",
|
||||
"exportedTo": "Експортовано в {}",
|
||||
@ -94,14 +94,14 @@
|
||||
"obtainiumImport": "Імпорт в Obtainium",
|
||||
"importFromURLList": "Імпорт зі списку URL-адрес",
|
||||
"searchQuery": "Пошуковий запит",
|
||||
"appURLList": "Список URL-адрес додатків",
|
||||
"appURLList": "Список URL-адрес застосунків",
|
||||
"line": "Лінія",
|
||||
"searchX": "Пошук {}",
|
||||
"noResults": "Результати відсутні",
|
||||
"importX": "Імпорт {}",
|
||||
"importedAppsIdDisclaimer": "Імпортовані додатки можуть неправильно відображатися як \"Не встановлені\".\nДля виправлення цього перевстановіть їх через Obtainium.\nЦе не повинно вплинути на дані додатків.\n\nПов'язано лише з URL-адресами та імпортом від третіх сторін.",
|
||||
"importedAppsIdDisclaimer": "Імпортовані застосунки можуть неправильно відображатися як \"Не встановлені\".\nДля виправлення цього перевстановіть їх через Obtainium.\nЦе не повинно вплинути на дані застосунків.\n\nПов'язано лише з URL-адресами та імпортом від третіх сторін.",
|
||||
"importErrors": "Помилки імпорту",
|
||||
"importedXOfYApps": "Імпортовано {} з {} додатків.",
|
||||
"importedXOfYApps": "Імпортовано {} з {} застосунків.",
|
||||
"followingURLsHadErrors": "Помилки в наступних URL-адресах:",
|
||||
"selectURL": "Вибрати URL",
|
||||
"selectURLs": "Вибрати URL-адреси",
|
||||
@ -110,19 +110,19 @@
|
||||
"dark": "Темна",
|
||||
"light": "Світла",
|
||||
"followSystem": "Дотримуватися системи",
|
||||
"useBlackTheme": "Використовувати чисто чорну темну тему",
|
||||
"appSortBy": "Сортувати додатки за",
|
||||
"useBlackTheme": "Використовувати чорну тему (Amoled)",
|
||||
"appSortBy": "Сортувати застосунки за",
|
||||
"authorName": "Автор/Назва",
|
||||
"nameAuthor": "Назва/Автор",
|
||||
"asAdded": "За додаванням",
|
||||
"appSortOrder": "Порядок сортування додатків",
|
||||
"appSortOrder": "Порядок сортування застосунків",
|
||||
"ascending": "За зростанням",
|
||||
"descending": "За спаданням",
|
||||
"bgUpdateCheckInterval": "Інтервал перевірки оновлень у фоновому режимі",
|
||||
"neverManualOnly": "Ніколи - Тільки вручну",
|
||||
"appearance": "Вигляд",
|
||||
"showWebInAppView": "Показати джерело застосунку у вигляді веб-сторінки",
|
||||
"pinUpdates": "Закріпити оновлення у верхній частині вигляду додатків",
|
||||
"pinUpdates": "Закріпити оновлення у верхній частині вигляду застосунків",
|
||||
"updates": "Оновлення",
|
||||
"sourceSpecific": "Певне джерело",
|
||||
"appSource": "Джерело застосунку",
|
||||
@ -139,23 +139,23 @@
|
||||
"warning": "Попередження",
|
||||
"sourceIsXButPackageFromYPrompt": "Джерело застосунку - '{}' але пакет випуску походить з '{}'. Продовжити?",
|
||||
"updatesAvailable": "Доступні оновлення",
|
||||
"updatesAvailableNotifDescription": "Повідомляє користувача, що доступні оновлення для одного чи декількох додатків, які відстежує Obtainium",
|
||||
"updatesAvailableNotifDescription": "Повідомляє користувача, що доступні оновлення для одного чи декількох застосунків, які відстежує Obtainium",
|
||||
"noNewUpdates": "Немає нових оновлень.",
|
||||
"xHasAnUpdate": "{} має оновлення.",
|
||||
"appsUpdated": "Додатки оновлено",
|
||||
"appsUpdatedNotifDescription": "Повідомляє користувача, що оновлення одного чи декількох додатків було застосовано в фоновому режимі",
|
||||
"appsUpdated": "Застосунки оновлено",
|
||||
"appsUpdatedNotifDescription": "Повідомляє користувача, що оновлення одного чи декількох застосунків було застосовано в фоновому режимі",
|
||||
"xWasUpdatedToY": "{} було оновлено до {}.",
|
||||
"errorCheckingUpdates": "Помилка перевірки оновлень",
|
||||
"errorCheckingUpdatesNotifDescription": "Повідомлення, яке з'являється, коли перевірка оновлень в фоновому режимі завершується невдачею",
|
||||
"appsRemoved": "Додатки видалено",
|
||||
"appsRemovedNotifDescription": "Повідомляє користувача, що один чи декілька додатків були видалені через помилки при завантаженні",
|
||||
"appsRemoved": "Застосунки видалено",
|
||||
"appsRemovedNotifDescription": "Повідомляє користувача, що один чи декілька застосунків були видалені через помилки при завантаженні",
|
||||
"xWasRemovedDueToErrorY": "{} було видалено через цю помилку: {}",
|
||||
"completeAppInstallation": "Завершення установки застосунку",
|
||||
"obtainiumMustBeOpenToInstallApps": "Для встановлення додатків Obtainium має бути відкритий",
|
||||
"obtainiumMustBeOpenToInstallApps": "Для встановлення застосунків Obtainium має бути відкритий",
|
||||
"completeAppInstallationNotifDescription": "Прохання користувача повернутися до Obtainium для завершення установки застосунку",
|
||||
"checkingForUpdates": "Перевірка оновлень",
|
||||
"checkingForUpdatesNotifDescription": "Тимчасове повідомлення, яке з'являється при перевірці оновлень",
|
||||
"pleaseAllowInstallPerm": "Будь ласка, дозвольте Obtainium встановлювати додатки",
|
||||
"pleaseAllowInstallPerm": "Будь ласка, дозвольте Obtainium встановлювати застосунки",
|
||||
"trackOnly": "Тільки відстеження",
|
||||
"errorWithHttpStatusCode": "Помилка {} HTTP-коду",
|
||||
"versionCorrectionDisabled": "Виправлення версії вимкнено (здається, плагін не працює)",
|
||||
@ -171,7 +171,7 @@
|
||||
"appIdOrName": "Ідентифікатор або назва застосунку",
|
||||
"appId": "Ідентифікатор застосунку",
|
||||
"appWithIdOrNameNotFound": "Застосунок з таким ідентифікатором або назвою не знайдено",
|
||||
"reposHaveMultipleApps": "Сховища можуть містити кілька додатків",
|
||||
"reposHaveMultipleApps": "Сховища можуть містити кілька застосунків",
|
||||
"fdroidThirdPartyRepo": "F-Droid Стороннє сховище",
|
||||
"steamMobile": "Мобільний Steam",
|
||||
"steamChat": "Чат Steam",
|
||||
@ -181,7 +181,7 @@
|
||||
"markUpdated": "Позначити як оновлене",
|
||||
"additionalOptions": "Додаткові опції",
|
||||
"disableVersionDetection": "Вимкнути визначення версії",
|
||||
"noVersionDetectionExplanation": "Цю опцію слід використовувати лише для додатків, де визначення версії працює неправильно.",
|
||||
"noVersionDetectionExplanation": "Цю опцію слід використовувати лише для застосунків, де визначення версії працює неправильно.",
|
||||
"downloadingX": "Завантаження {}",
|
||||
"downloadNotifDescription": "Повідомляє користувача про прогрес завантаження застосунку",
|
||||
"noAPKFound": "APK не знайдено",
|
||||
@ -192,19 +192,19 @@
|
||||
"noCategory": "Без категорії",
|
||||
"noCategories": "Немає категорій",
|
||||
"deleteCategoriesQuestion": "Видалити категорії?",
|
||||
"categoryDeleteWarning": "Усі додатки у видалених категоріях будуть переведені у некатегоризовані.",
|
||||
"categoryDeleteWarning": "Усі застосунки у видалених категоріях будуть переведені у некатегоризовані.",
|
||||
"addCategory": "Додати категорію",
|
||||
"label": "Мітка",
|
||||
"language": "Мова",
|
||||
"copiedToClipboard": "Скопійовано в буфер обміну",
|
||||
"storagePermissionDenied": "Відмовлено у дозволі на доступ до сховища",
|
||||
"selectedCategorizeWarning": "Це замінить будь-які існуючі налаштування категорій для вибраних додатків.",
|
||||
"selectedCategorizeWarning": "Це замінить будь-які існуючі налаштування категорій для вибраних застосунків.",
|
||||
"filterAPKsByRegEx": "Фільтрувати APK за регулярним виразом",
|
||||
"removeFromObtainium": "Видалити з Obtainium",
|
||||
"uninstallFromDevice": "Видалити з пристрою",
|
||||
"onlyWorksWithNonVersionDetectApps": "Працює лише з застосунками з вимкненим визначенням версії.",
|
||||
"releaseDateAsVersion": "Використовувати дату випуску як рядок версії",
|
||||
"releaseDateAsVersionExplanation": "Цю опцію слід використовувати лише для додатків, де визначення версії працює неправильно, але є дата випуску.",
|
||||
"releaseDateAsVersionExplanation": "Цю опцію слід використовувати лише для застосунків, де визначення версії працює неправильно, але є дата випуску.",
|
||||
"changes": "Зміни",
|
||||
"releaseDate": "Дата випуску",
|
||||
"importFromURLsInFile": "Імпорт з URL-адрес у файлі (наприклад, OPML)",
|
||||
@ -217,13 +217,13 @@
|
||||
"dontShowAgain": "Не показувати це знову",
|
||||
"dontShowTrackOnlyWarnings": "Не показувати попередження про 'Тільки відстеження'",
|
||||
"dontShowAPKOriginWarnings": "Не показувати попередження про походження APK",
|
||||
"moveNonInstalledAppsToBottom": "Перемістити невстановлені додатки вниз у перегляді додатків",
|
||||
"moveNonInstalledAppsToBottom": "Перемістити невстановлені застосунки вниз у перегляді застосунків",
|
||||
"gitlabPATLabel": "Особистий токен GitLab (Увімкнення пошуку та краще виявлення APK)",
|
||||
"about": "Про програму",
|
||||
"requiresCredentialsInSettings": "{} потребує додаткових облікових даних (у налаштуваннях)",
|
||||
"checkOnStart": "Перевірити наявність оновлень при запуску",
|
||||
"tryInferAppIdFromCode": "Спробувати вивести ідентифікатор застосунку з вихідного коду",
|
||||
"removeOnExternalUninstall": "Автоматично видаляти додатки, які було видалено зовнішнім чином",
|
||||
"removeOnExternalUninstall": "Автоматично видаляти застосунки, які було видалено зовнішнім чином",
|
||||
"pickHighestVersionCode": "Автоматично вибрати APK з найвищим кодом версії",
|
||||
"checkUpdateOnDetailPage": "Перевіряти наявність оновлень при відкритті сторінки деталей застосунку",
|
||||
"disablePageTransitions": "Вимкнути анімації переходів між сторінками",
|
||||
@ -232,15 +232,14 @@
|
||||
"addInfoBelow": "Додати цю інформацію нижче.",
|
||||
"addInfoInSettings": "Додати цю інформацію у налаштуваннях.",
|
||||
"githubSourceNote": "Лімітування швидкості GitHub можна уникнути, використовуючи ключ API.",
|
||||
"gitlabSourceNote": "Вилучення APK з GitLab може не працювати без ключа API.",
|
||||
"sortByLastLinkSegment": "Сортувати лише за останнім сегментом посилання",
|
||||
"filterReleaseNotesByRegEx": "Фільтрувати примітки до релізу за регулярним виразом",
|
||||
"customLinkFilterRegex": "Фільтр кастомного посилання на APK за регулярним виразом (за замовчуванням '.apk$')",
|
||||
"appsPossiblyUpdated": "Оновлення додатків спробовано",
|
||||
"appsPossiblyUpdatedNotifDescription": "Повідомляє користувача, що оновлення одного або декількох додатків можливо були застосовані в фоновому режимі",
|
||||
"appsPossiblyUpdated": "Спроб оновлення застосунків",
|
||||
"appsPossiblyUpdatedNotifDescription": "Повідомляє користувача, що оновлення одного або декількох застосунків можливо були застосовані в фоновому режимі",
|
||||
"xWasPossiblyUpdatedToY": "{} можливо було оновлено до {}.",
|
||||
"enableBackgroundUpdates": "Увімкнути оновлення в фоновому режимі",
|
||||
"backgroundUpdateReqsExplanation": "Оновлення в фоновому режимі може бути неможливим для всіх додатків.",
|
||||
"backgroundUpdateReqsExplanation": "Оновлення в фоновому режимі може бути неможливим для всіх застосунків.",
|
||||
"backgroundUpdateLimitsExplanation": "Успіх фонової установки може бути визначений лише після відкриття Obtainium.",
|
||||
"verifyLatestTag": "Перевірити тег 'latest'",
|
||||
"intermediateLinkRegex": "Фільтр для 'Проміжного' Посилання для Відвідування",
|
||||
@ -269,14 +268,14 @@
|
||||
"installing": "Встановлення",
|
||||
"skipUpdateNotifications": "Пропустити сповіщення про оновлення",
|
||||
"updatesAvailableNotifChannel": "Доступні оновлення",
|
||||
"appsUpdatedNotifChannel": "Додатки оновлені",
|
||||
"appsPossiblyUpdatedNotifChannel": "Спроба оновлення додатків",
|
||||
"appsUpdatedNotifChannel": "Застосунки оновлені",
|
||||
"appsPossiblyUpdatedNotifChannel": "Спроба оновлення застосунків",
|
||||
"errorCheckingUpdatesNotifChannel": "Помилка перевірки оновлень",
|
||||
"appsRemovedNotifChannel": "Додатки видалені",
|
||||
"appsRemovedNotifChannel": "Застосунки видалені",
|
||||
"downloadingXNotifChannel": "Завантаження {}",
|
||||
"completeAppInstallationNotifChannel": "Завершення встановлення застосунку",
|
||||
"checkingForUpdatesNotifChannel": "Перевірка оновлень",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Перевіряти лише встановлені та додатки, які відстежуються для оновлень",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Перевіряти лише встановлені та застосунки, які відстежуються для оновлень",
|
||||
"supportFixedAPKURL": "Підтримка фіксованих посилань на APK",
|
||||
"selectX": "Вибрати {}",
|
||||
"parallelDownloads": "Дозволити паралельні завантаження",
|
||||
@ -300,9 +299,11 @@
|
||||
"note": "Примітка",
|
||||
"selfHostedNote": "Випадаючий список \"{}\" може використовуватися для доступу до власних/призначених для самостійного використання екземплярів будь-якого джерела.",
|
||||
"badDownload": "APK не вдалося розпарсити (несумісний або часткове завантаження)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Діліться новими додатками з AppVerifier (якщо доступно)",
|
||||
"appVerifierInstructionToast": "Надішліть на AppVerifier, а потім поверніться сюди, коли будете готові.",
|
||||
"removeAppQuestion": {
|
||||
"one": "Видалити Застосунок?",
|
||||
"other": "Видалити додатки?"
|
||||
"one": "Видалити застосунок?",
|
||||
"other": "Видалити застосунки?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Забагато запитів (обмеження швидкості) - повторіть спробу через {} хвилину",
|
||||
@ -313,44 +314,44 @@
|
||||
"other": "Помилка перевірки оновлень у фоновому режимі - спробую знову через {} хвилин"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "Фонова перевірка оновлень знайшла {} оновлення - сповістить користувача, якщо це необхідно",
|
||||
"other": "Фонова перевірка оновлень знайшла {} оновлень - сповістить користувача, якщо це необхідно"
|
||||
"one": "Фонова перевірка оновлень знайшла {} оновлення - сповістити користувача, якщо це необхідно",
|
||||
"other": "Фонова перевірка оновлень знайшла {} оновлень - сповістити користувача, якщо це необхідно"
|
||||
},
|
||||
"apps": {
|
||||
"one": "{} Застосунок",
|
||||
"other": "{} Додатки"
|
||||
"one": "{} застосунок",
|
||||
"other": "{} застосунки"
|
||||
},
|
||||
"url": {
|
||||
"one": "{} URL",
|
||||
"one": "{} URL-адреса",
|
||||
"other": "{} URL-адреси"
|
||||
},
|
||||
"minute": {
|
||||
"one": "{} Хвилина",
|
||||
"other": "{} Хвилин"
|
||||
"one": "{} хвилина",
|
||||
"other": "{} хвилин"
|
||||
},
|
||||
"hour": {
|
||||
"one": "{} Година",
|
||||
"other": "{} Годин"
|
||||
"one": "{} година",
|
||||
"other": "{} годин"
|
||||
},
|
||||
"day": {
|
||||
"one": "{} День",
|
||||
"other": "{} Днів"
|
||||
"one": "{} день",
|
||||
"other": "{} днів"
|
||||
},
|
||||
"clearedNLogsBeforeXAfterY": {
|
||||
"one": "Очищено {n} журнал (до = {before}, після = {after})",
|
||||
"other": "Очищено {n} журналів (до = {before}, після = {after})"
|
||||
},
|
||||
"xAndNMoreUpdatesAvailable": {
|
||||
"one": "{} і 1 інше Застосунок мають оновлення.",
|
||||
"other": "{} і {} інших додатки мають оновлення."
|
||||
"one": "{} та ще 1 застосунок мають оновлення.",
|
||||
"other": "{} та ще {} застосунки мають оновлення."
|
||||
},
|
||||
"xAndNMoreUpdatesInstalled": {
|
||||
"one": "{} і 1 інше Застосунок було оновлено.",
|
||||
"other": "{} і {} інших додатків було оновлено."
|
||||
"one": "{} та ще 1 застосунок було оновлено.",
|
||||
"other": "{} та ще {} застосунків було оновлено."
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} і 1 інше Застосунок можливо було оновлено.",
|
||||
"other": "{} і {} інших додатків можливо було оновлено."
|
||||
"one": "{} та ще 1 застосунок можливо було оновлено.",
|
||||
"other": "{} та ще {} застосунків можливо було оновлено."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
|
@ -299,6 +299,8 @@
|
||||
"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?"
|
||||
|
@ -299,6 +299,8 @@
|
||||
"note": "备注",
|
||||
"selfHostedNote": "可以通过“{}”下拉菜单来指向任意来源的自托管/自定义实例。",
|
||||
"badDownload": "无法解析 APK 文件(不兼容或文件不完整)",
|
||||
"beforeNewInstallsShareToAppVerifier": "与 AppVerifier 共享新应用程序(如有)",
|
||||
"appVerifierInstructionToast": "分享到 AppVerifier,准备就绪后返回此处。",
|
||||
"removeAppQuestion": {
|
||||
"one": "是否删除应用?",
|
||||
"other": "是否删除应用?"
|
||||
|
@ -121,9 +121,11 @@ class GitLab extends AppSource {
|
||||
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?$optionalAuth',
|
||||
'https://${hosts[0]}/api/v4/projects/${names.author}%2F${names.name}/${trackOnly ? 'repository/tags' : 'releases'}?$optionalAuth',
|
||||
additionalSettings);
|
||||
if (res.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
@ -152,9 +154,8 @@ 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()),
|
||||
@ -164,18 +165,19 @@ class GitLab extends AppSource {
|
||||
if (apkDetailsList.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
var finalResult = apkDetailsList.first;
|
||||
|
||||
// Fallback procedure
|
||||
bool fallbackToOlderReleases =
|
||||
additionalSettings['fallbackToOlderReleases'] == true;
|
||||
if (fallbackToOlderReleases) {
|
||||
if (additionalSettings['trackOnly'] != true) {
|
||||
apkDetailsList =
|
||||
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
|
||||
}
|
||||
if (apkDetailsList.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
if (finalResult.apkUrls.isEmpty && fallbackToOlderReleases && !trackOnly) {
|
||||
apkDetailsList =
|
||||
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
|
||||
finalResult = apkDetailsList.first;
|
||||
}
|
||||
|
||||
if (finalResult.apkUrls.isEmpty && !trackOnly) {
|
||||
throw NoAPKError();
|
||||
}
|
||||
|
||||
return apkDetailsList.first;
|
||||
|
@ -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';
|
||||
@ -108,6 +109,7 @@ class _AppPageState extends State<AppPage> {
|
||||
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,
|
||||
@ -125,13 +127,26 @@ class _AppPageState extends State<AppPage> {
|
||||
.textTheme
|
||||
.bodyLarge!
|
||||
.copyWith(fontWeight: FontWeight.bold)),
|
||||
app?.app.releaseDate == null
|
||||
? const SizedBox.shrink()
|
||||
: Text(
|
||||
app!.app.releaseDate.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
),
|
||||
changeLogFn != null || app?.app.releaseDate != null
|
||||
? GestureDetector(
|
||||
onTap: changeLogFn,
|
||||
child: Text(
|
||||
app?.app.releaseDate == null
|
||||
? tr('changes')
|
||||
: app!.app.releaseDate.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
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,
|
||||
),
|
||||
@ -361,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] : [],
|
||||
@ -368,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;
|
||||
|
@ -351,6 +351,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: [
|
||||
@ -363,6 +379,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;
|
||||
})
|
||||
],
|
||||
),
|
||||
installMethodDropdown,
|
||||
height32,
|
||||
Text(
|
||||
@ -474,22 +527,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)) {
|
||||
File tempDownloadedFile = File('${downloadedFile.path}.part');
|
||||
if (tempDownloadedFile.existsSync()) {
|
||||
tempDownloadedFile.deleteSync(recursive: true);
|
||||
}
|
||||
var length = response.contentLength;
|
||||
var received = 0;
|
||||
double? progress;
|
||||
var sink = tempDownloadedFile.openWrite();
|
||||
await response.stream.map((s) {
|
||||
received += s.length;
|
||||
progress = (length != null ? received / length * 100 : 30);
|
||||
if (onProgress != null) {
|
||||
onProgress(progress);
|
||||
|
||||
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;
|
||||
}
|
||||
return s;
|
||||
}).pipe(sink);
|
||||
await sink.close();
|
||||
progress = null;
|
||||
if (length > fullContentLength) {
|
||||
useExisting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Download to a '.temp' file (to distinguish btn. complete/incomplete files)
|
||||
File tempDownloadedFile = File('${downloadedFile.path}.part');
|
||||
|
||||
// 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);
|
||||
}
|
||||
sink ??= tempDownloadedFile.openWrite(mode: FileMode.writeOnly);
|
||||
|
||||
// Perform the download
|
||||
var received = 0;
|
||||
double? progress;
|
||||
if (rangeStart > 0 && fullContentLength != null) {
|
||||
received = rangeStart;
|
||||
}
|
||||
await response.stream.map((s) {
|
||||
received += s.length;
|
||||
progress =
|
||||
(fullContentLength != null ? (received / fullContentLength) * 100 : 30);
|
||||
if (onProgress != null) {
|
||||
onProgress(progress);
|
||||
}
|
||||
if (response.statusCode != 200) {
|
||||
tempDownloadedFile.deleteSync(recursive: true);
|
||||
throw response.reasonPhrase ?? tr('unexpectedError');
|
||||
}
|
||||
if (tempDownloadedFile.existsSync()) {
|
||||
tempDownloadedFile.renameSync(downloadedFile.path);
|
||||
}
|
||||
} else {
|
||||
client.close();
|
||||
return s;
|
||||
}).pipe(sink);
|
||||
await sink.close();
|
||||
bool likelyCorruptFile = (progress ?? 0) > 101;
|
||||
progress = null;
|
||||
if (onProgress != null) {
|
||||
onProgress(progress);
|
||||
}
|
||||
if (response.statusCode < 200 ||
|
||||
response.statusCode > 299 ||
|
||||
likelyCorruptFile) {
|
||||
tempDownloadedFile.deleteSync(recursive: true);
|
||||
throw response.reasonPhrase ?? tr('unexpectedError');
|
||||
}
|
||||
if (tempDownloadedFile.existsSync()) {
|
||||
tempDownloadedFile.renameSync(downloadedFile.path);
|
||||
}
|
||||
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
|
||||
var somethingInstalled = false;
|
||||
try {
|
||||
var somethingInstalled = false;
|
||||
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) {
|
||||
@ -577,7 +664,13 @@ class AppsProvider with ChangeNotifier {
|
||||
}
|
||||
bool installed = false;
|
||||
if (code != null && code != 0 && code != 3) {
|
||||
throw InstallError(code);
|
||||
try {
|
||||
file.file.deleteSync(recursive: true);
|
||||
} catch (e) {
|
||||
//
|
||||
} finally {
|
||||
throw InstallError(code);
|
||||
}
|
||||
} else if (code == 0) {
|
||||
installed = true;
|
||||
apps[file.appId]!.app.installedVersion =
|
||||
@ -758,17 +851,24 @@ 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) {
|
||||
@ -776,7 +876,9 @@ class AppsProvider with ChangeNotifier {
|
||||
[apps[id]!.app],
|
||||
id: id.hashCode));
|
||||
}
|
||||
installedIds.add(id);
|
||||
if (sayInstalled) {
|
||||
installedIds.add(id);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
apps[id]?.downloadProgress = null;
|
||||
|
@ -479,4 +479,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(),
|
||||
|
76
pubspec.lock
76
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:
|
||||
@ -38,10 +38,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: app_links
|
||||
sha256: "3ced568a5d9e309e99af71285666f1f3117bddd0bd5b3317979dccc1a40cada4"
|
||||
sha256: "42dc15aecf2618ace4ffb74a2e58a50e45cd1b9f2c17c8f0cafe4c297f08c815"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.5.1"
|
||||
version: "4.0.1"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -70,10 +70,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: background_fetch
|
||||
sha256: eb3af263d390d7e68ecb90f2ae984d2bfd96dceb4c7b4f72418dd5383b49de0a
|
||||
sha256: dbffec0317ccdef6e2014cb543e147f52441e29c4fcb53dfd23558c4d92ddece
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.4"
|
||||
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:
|
||||
@ -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:
|
||||
@ -254,10 +254,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: caa6bc229eab3e32eb2f37b53a5f9d22a6981474afd210c512a7546c1e1a04f6
|
||||
sha256: d1d0ac3966b36dc3e66eeefb40280c17feb87fa2099c6e22e6a1fc959327bd03
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.0"
|
||||
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: "55b9b229307a10974b26296ff29f2e132256ba4bd74266939118eaefa941cb00"
|
||||
sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "16.3.3"
|
||||
version: "17.0.0"
|
||||
flutter_local_notifications_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -336,10 +336,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: cb44f7831b23a6bdd0f501718b0d2e8045cbc625a15f668af37ddb80314821db
|
||||
sha256: "87e11b9df25a42e2db315b8b7a51fae8e66f57a4b2f50ec4b822d0fa155e6b52"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.21"
|
||||
version: "0.6.22"
|
||||
flutter_plugin_android_lifecycle:
|
||||
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:
|
||||
@ -586,10 +586,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44"
|
||||
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.3.0"
|
||||
version: "11.3.1"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -602,10 +602,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: bdafc6db74253abb63907f4e357302e6bb786ab41465e8635f362ee71fd8707b
|
||||
sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.4.0"
|
||||
version: "9.4.4"
|
||||
permission_handler_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -618,10 +618,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78"
|
||||
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "4.2.1"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -674,18 +674,18 @@ packages:
|
||||
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:
|
||||
@ -783,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:
|
||||
@ -959,10 +959,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: "3e5f4e9d818086b0d01a66fb1ff9cc72ab0cc58c71980e3d3661c5685ea0efb0"
|
||||
sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.15.0"
|
||||
version: "3.16.0"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -975,10 +975,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: "9bf168bccdf179ce90450b5f37e36fe263f591c9338828d6bf09b6f8d0f57f86"
|
||||
sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.12.0"
|
||||
version: "3.13.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
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.5+2255 # 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