mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-08-27 00:19:29 +02:00
Compare commits
14 Commits
v0.14.34-b
...
v0.14.35-b
Author | SHA1 | Date | |
---|---|---|---|
|
64533f7a3f | ||
|
0b7de8d387 | ||
|
8eba4860fe | ||
|
b53e2f57e6 | ||
|
e1e834297b | ||
|
e37dc6e341 | ||
|
c91c896854 | ||
|
7e5dfa03d6 | ||
|
1a4ec3f049 | ||
|
756763fcbe | ||
|
93036c4e67 | ||
|
15bf972ef6 | ||
|
bcb4567382 | ||
|
3890c4ffb9 |
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -21,6 +21,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build APKs
|
- name: Build APKs
|
||||||
run: |
|
run: |
|
||||||
|
sed -i 's/signingConfig signingConfigs.release//g' android/app/build.gradle
|
||||||
flutter build apk --flavor normal && flutter build apk --split-per-abi --flavor normal
|
flutter build apk --flavor normal && flutter build apk --split-per-abi --flavor normal
|
||||||
for file in build/app/outputs/flutter-apk/app-*normal*.apk*; do mv "$file" "${file//-normal/}"; done
|
for file in build/app/outputs/flutter-apk/app-*normal*.apk*; do mv "$file" "${file//-normal/}"; done
|
||||||
rm ./build/app/outputs/flutter-apk/*.sha1
|
rm ./build/app/outputs/flutter-apk/*.sha1
|
||||||
|
@@ -71,9 +71,17 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Nije instalirano",
|
"notInstalled": "Nije instalirano",
|
||||||
"estimateInBrackets": "(Procjena)",
|
"estimateInBrackets": "(Procjena)",
|
||||||
"selectAll": "Označi sve",
|
"selectAll": "Označi sve",
|
||||||
"deselectN": "Poništi odabir {}",
|
"deselectX": "Poništi odabir {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} će biti uklonjen iz Obtainiuma, ali će ostati instaliran na uređaju.",
|
"xWillBeRemovedButRemainInstalled": "{} će biti uklonjen iz Obtainiuma, ali će ostati instaliran na uređaju.",
|
||||||
"removeSelectedAppsQuestion": "Želite li ukloniti odabrane aplikacije?",
|
"removeSelectedAppsQuestion": "Želite li ukloniti odabrane aplikacije?",
|
||||||
"removeSelectedApps": "Ukloni odabrane aplikacije",
|
"removeSelectedApps": "Ukloni odabrane aplikacije",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija",
|
"moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija",
|
||||||
"gitlabPATLabel": "GitLab token za lični pristup\n(Omogućava pretraživanje i bolje otkrivanje APK-a)",
|
"gitlabPATLabel": "GitLab token za lični pristup\n(Omogućava pretraživanje i bolje otkrivanje APK-a)",
|
||||||
"about": "O nama",
|
"about": "O nama",
|
||||||
"requiresCredentialsInSettings": "Za ovo su potrebni dodatni akreditivi (u Postavkama)",
|
"requiresCredentialsInSettings": "{}: Za ovo su potrebni dodatni akreditivi (u Postavkama)",
|
||||||
"checkOnStart": "Provjerite ima li novosti pri pokretanju",
|
"checkOnStart": "Provjerite ima li novosti pri pokretanju",
|
||||||
"tryInferAppIdFromCode": "Pokušati otkriti ID aplikacije iz izvornog koda",
|
"tryInferAppIdFromCode": "Pokušati otkriti ID aplikacije iz izvornog koda",
|
||||||
"removeOnExternalUninstall": "Automatski ukloni eksterno deinstalirane aplikacije",
|
"removeOnExternalUninstall": "Automatski ukloni eksterno deinstalirane aplikacije",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Dovršite instalaciju aplikacije",
|
"completeAppInstallationNotifChannel": "Dovršite instalaciju aplikacije",
|
||||||
"checkingForUpdatesNotifChannel": "Tražim moguće nadogradnje",
|
"checkingForUpdatesNotifChannel": "Tražim moguće nadogradnje",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Želite li ukloniti aplikaciju?",
|
"one": "Želite li ukloniti aplikaciju?",
|
||||||
"other": "Želite li ukloniti aplikacije?"
|
"other": "Želite li ukloniti aplikacije?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Není nainstalováno",
|
"notInstalled": "Není nainstalováno",
|
||||||
"estimateInBrackets": "(přibližně)",
|
"estimateInBrackets": "(přibližně)",
|
||||||
"selectAll": "Vybrat Vše",
|
"selectAll": "Vybrat Vše",
|
||||||
"deselectN": "{} deselected",
|
"deselectX": "{} deselected",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} bude odstraněn z Obtainium, ale zůstane nainstalován v zařízení.",
|
"xWillBeRemovedButRemainInstalled": "{} bude odstraněn z Obtainium, ale zůstane nainstalován v zařízení.",
|
||||||
"removeSelectedAppsQuestion": "Odebrat vybrané aplikace?",
|
"removeSelectedAppsQuestion": "Odebrat vybrané aplikace?",
|
||||||
"removeSelectedApps": "Odebrat vybrané aplikace",
|
"removeSelectedApps": "Odebrat vybrané aplikace",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace",
|
"moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Umožňuje vyhledávání a lepší zjišťování APK)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Umožňuje vyhledávání a lepší zjišťování APK)",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"requiresCredentialsInSettings": "Vyžaduje další pověření (v nastavení)",
|
"requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)",
|
||||||
"checkOnStart": "Zkontrolovat jednou při spuštění",
|
"checkOnStart": "Zkontrolovat jednou při spuštění",
|
||||||
"tryInferAppIdFromCode": "Pokusit se určit ID aplikace ze zdrojového kódu",
|
"tryInferAppIdFromCode": "Pokusit se určit ID aplikace ze zdrojového kódu",
|
||||||
"removeOnExternalUninstall": "Automaticky odstranit externě odinstalované aplikace",
|
"removeOnExternalUninstall": "Automaticky odstranit externě odinstalované aplikace",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Dokončit instalaci aplikace",
|
"completeAppInstallationNotifChannel": "Dokončit instalaci aplikace",
|
||||||
"checkingForUpdatesNotifChannel": "Zkontrolovat aktualizace",
|
"checkingForUpdatesNotifChannel": "Zkontrolovat aktualizace",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Odstranit Apku?",
|
"one": "Odstranit Apku?",
|
||||||
"other": "Odstranit Apky?"
|
"other": "Odstranit Apky?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Nicht installiert",
|
"notInstalled": "Nicht installiert",
|
||||||
"estimateInBrackets": "(Ungefähr)",
|
"estimateInBrackets": "(Ungefähr)",
|
||||||
"selectAll": "Alle auswählen",
|
"selectAll": "Alle auswählen",
|
||||||
"deselectN": "{} abgewählt",
|
"deselectX": "{} abgewählt",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} wird aus Obtainium entfernt, bleibt aber auf dem Gerät installiert.",
|
"xWillBeRemovedButRemainInstalled": "{} wird aus Obtainium entfernt, bleibt aber auf dem Gerät installiert.",
|
||||||
"removeSelectedAppsQuestion": "Ausgewählte Apps entfernen?",
|
"removeSelectedAppsQuestion": "Ausgewählte Apps entfernen?",
|
||||||
"removeSelectedApps": "Ausgewählte Apps entfernen",
|
"removeSelectedApps": "Ausgewählte Apps entfernen",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben",
|
"moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Aktiviert Suche und bessere APK Entdeckung)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Aktiviert Suche und bessere APK Entdeckung)",
|
||||||
"about": "Über",
|
"about": "Über",
|
||||||
"requiresCredentialsInSettings": "Benötigt zusätzliche Anmeldedaten (in den Einstellungen)",
|
"requiresCredentialsInSettings": "{}: Benötigt zusätzliche Anmeldedaten (in den Einstellungen)",
|
||||||
"checkOnStart": "Überprüfe einmalig beim Start",
|
"checkOnStart": "Überprüfe einmalig beim Start",
|
||||||
"tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln",
|
"tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln",
|
||||||
"removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps",
|
"removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "App Installation abschließen",
|
"completeAppInstallationNotifChannel": "App Installation abschließen",
|
||||||
"checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen",
|
"checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen",
|
"onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "App entfernen?",
|
"one": "App entfernen?",
|
||||||
"other": "Apps entfernen?"
|
"other": "Apps entfernen?"
|
||||||
@@ -327,4 +329,4 @@
|
|||||||
"one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
|
"one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
|
||||||
"other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
|
"other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Not Installed",
|
"notInstalled": "Not Installed",
|
||||||
"estimateInBrackets": "(Estimate)",
|
"estimateInBrackets": "(Estimate)",
|
||||||
"selectAll": "Select All",
|
"selectAll": "Select All",
|
||||||
"deselectN": "Deselect {}",
|
"deselectX": "Deselect {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} will be removed from Obtainium but remain installed on device.",
|
"xWillBeRemovedButRemainInstalled": "{} will be removed from Obtainium but remain installed on device.",
|
||||||
"removeSelectedAppsQuestion": "Remove Selected Apps?",
|
"removeSelectedAppsQuestion": "Remove Selected Apps?",
|
||||||
"removeSelectedApps": "Remove Selected Apps",
|
"removeSelectedApps": "Remove Selected Apps",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
|
"requiresCredentialsInSettings": "{} needs additional credentials (in Settings)",
|
||||||
"checkOnStart": "Check for updates on startup",
|
"checkOnStart": "Check for updates on startup",
|
||||||
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
||||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Complete App Installation",
|
"completeAppInstallationNotifChannel": "Complete App Installation",
|
||||||
"checkingForUpdatesNotifChannel": "Checking for Updates",
|
"checkingForUpdatesNotifChannel": "Checking for Updates",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remove App?",
|
"one": "Remove App?",
|
||||||
"other": "Remove Apps?"
|
"other": "Remove Apps?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "No Instalado",
|
"notInstalled": "No Instalado",
|
||||||
"estimateInBrackets": "(Aproximado)",
|
"estimateInBrackets": "(Aproximado)",
|
||||||
"selectAll": "Seleccionar Todo",
|
"selectAll": "Seleccionar Todo",
|
||||||
"deselectN": "Deseleccionar {}",
|
"deselectX": "Deseleccionar {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} será borrada de Obtainium pero continuará instalada en el dispositivo.",
|
"xWillBeRemovedButRemainInstalled": "{} será borrada de Obtainium pero continuará instalada en el dispositivo.",
|
||||||
"removeSelectedAppsQuestion": "¿Borrar aplicaciones seleccionadas?",
|
"removeSelectedAppsQuestion": "¿Borrar aplicaciones seleccionadas?",
|
||||||
"removeSelectedApps": "Borrar Aplicaciones Seleccionadas",
|
"removeSelectedApps": "Borrar Aplicaciones Seleccionadas",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
|
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
|
||||||
"checkOnStart": "Check for updates on startup",
|
"checkOnStart": "Check for updates on startup",
|
||||||
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
||||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación",
|
"completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación",
|
||||||
"checkingForUpdatesNotifChannel": "Buscando Actualizaciones",
|
"checkingForUpdatesNotifChannel": "Buscando Actualizaciones",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "¿Eliminar Aplicación?",
|
"one": "¿Eliminar Aplicación?",
|
||||||
"other": "¿Eliminar Aplicaciones?"
|
"other": "¿Eliminar Aplicaciones?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "نصب نشده",
|
"notInstalled": "نصب نشده",
|
||||||
"estimateInBrackets": "(تخمین زدن)",
|
"estimateInBrackets": "(تخمین زدن)",
|
||||||
"selectAll": "انتخاب همه",
|
"selectAll": "انتخاب همه",
|
||||||
"deselectN": "لغو انتخاب {}",
|
"deselectX": "لغو انتخاب {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} از Obtainium حذف میشود اما روی دستگاه نصب میماند.",
|
"xWillBeRemovedButRemainInstalled": "{} از Obtainium حذف میشود اما روی دستگاه نصب میماند.",
|
||||||
"removeSelectedAppsQuestion": "برنامه های انتخابی حذف شود؟",
|
"removeSelectedAppsQuestion": "برنامه های انتخابی حذف شود؟",
|
||||||
"removeSelectedApps": "حذف برنامه های انتخاب شده",
|
"removeSelectedApps": "حذف برنامه های انتخاب شده",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید",
|
"moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید",
|
||||||
"gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو و کشف بهتر APK را فعال میکند)",
|
"gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو و کشف بهتر APK را فعال میکند)",
|
||||||
"about": "درباره",
|
"about": "درباره",
|
||||||
"requiresCredentialsInSettings": "این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)",
|
"requiresCredentialsInSettings": "{}: این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)",
|
||||||
"checkOnStart": "بررسی در شروع",
|
"checkOnStart": "بررسی در شروع",
|
||||||
"tryInferAppIdFromCode": "شناسه برنامه را از کد منبع استنباط کنید",
|
"tryInferAppIdFromCode": "شناسه برنامه را از کد منبع استنباط کنید",
|
||||||
"removeOnExternalUninstall": "حذف خودکار برنامه های حذف نصب شده خارجی",
|
"removeOnExternalUninstall": "حذف خودکار برنامه های حذف نصب شده خارجی",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "نصب کامل برنامه",
|
"completeAppInstallationNotifChannel": "نصب کامل برنامه",
|
||||||
"checkingForUpdatesNotifChannel": "بررسی بهروزرسانیها",
|
"checkingForUpdatesNotifChannel": "بررسی بهروزرسانیها",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "برنامه حذف شود؟",
|
"one": "برنامه حذف شود؟",
|
||||||
"other": "برنامه ها حذف شوند؟"
|
"other": "برنامه ها حذف شوند؟"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Pas installé",
|
"notInstalled": "Pas installé",
|
||||||
"estimateInBrackets": "(Estimation)",
|
"estimateInBrackets": "(Estimation)",
|
||||||
"selectAll": "Tout sélectionner",
|
"selectAll": "Tout sélectionner",
|
||||||
"deselectN": "Déselectionner {}",
|
"deselectX": "Déselectionner {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} sera supprimé d'Obtainium mais restera installé sur l'appareil.",
|
"xWillBeRemovedButRemainInstalled": "{} sera supprimé d'Obtainium mais restera installé sur l'appareil.",
|
||||||
"removeSelectedAppsQuestion": "Supprimer les applications sélectionnées ?",
|
"removeSelectedAppsQuestion": "Supprimer les applications sélectionnées ?",
|
||||||
"removeSelectedApps": "Supprimer les applications sélectionnées",
|
"removeSelectedApps": "Supprimer les applications sélectionnées",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
|
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
|
||||||
"checkOnStart": "Check for updates on startup",
|
"checkOnStart": "Check for updates on startup",
|
||||||
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
||||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Installation complète de l'application",
|
"completeAppInstallationNotifChannel": "Installation complète de l'application",
|
||||||
"checkingForUpdatesNotifChannel": "Vérification des mises à jour",
|
"checkingForUpdatesNotifChannel": "Vérification des mises à jour",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Supprimer l'application ?",
|
"one": "Supprimer l'application ?",
|
||||||
"other": "Supprimer les applications ?"
|
"other": "Supprimer les applications ?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Nem telepített",
|
"notInstalled": "Nem telepített",
|
||||||
"estimateInBrackets": "(Becslés)",
|
"estimateInBrackets": "(Becslés)",
|
||||||
"selectAll": "Mindet kiválaszt",
|
"selectAll": "Mindet kiválaszt",
|
||||||
"deselectN": "Törölje {} kijelölését",
|
"deselectX": "Törölje {} kijelölését",
|
||||||
"xWillBeRemovedButRemainInstalled": "A(z) {} el lesz távolítva az Obtainiumból, de továbbra is telepítve marad az eszközön.",
|
"xWillBeRemovedButRemainInstalled": "A(z) {} el lesz távolítva az Obtainiumból, de továbbra is telepítve marad az eszközön.",
|
||||||
"removeSelectedAppsQuestion": "Eltávolítja a kiválasztott appokat?",
|
"removeSelectedAppsQuestion": "Eltávolítja a kiválasztott appokat?",
|
||||||
"removeSelectedApps": "Távolítsa el a kiválasztott appokat",
|
"removeSelectedApps": "Távolítsa el a kiválasztott appokat",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Helyezze át a nem telepített appokat az App nézet aljára",
|
"moveNonInstalledAppsToBottom": "Helyezze át a nem telepített appokat az App nézet aljára",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Engedélyezi a Keresést és jobb APK felfedezés)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Engedélyezi a Keresést és jobb APK felfedezés)",
|
||||||
"about": "Rólunk",
|
"about": "Rólunk",
|
||||||
"requiresCredentialsInSettings": "Ehhez további hitelesítő adatokra van szükség (a Beállításokban)",
|
"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",
|
"checkOnStart": "Egyszer az alkalmazás indításakor is",
|
||||||
"tryInferAppIdFromCode": "Próbálja kikövetkeztetni az app azonosítót a forráskódból",
|
"tryInferAppIdFromCode": "Próbálja kikövetkeztetni az app azonosítót a forráskódból",
|
||||||
"removeOnExternalUninstall": "A külsőleg eltávolított appok auto. eltávolítása",
|
"removeOnExternalUninstall": "A külsőleg eltávolított appok auto. eltávolítása",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Teljes app telepítés",
|
"completeAppInstallationNotifChannel": "Teljes app telepítés",
|
||||||
"checkingForUpdatesNotifChannel": "Frissítések keresése",
|
"checkingForUpdatesNotifChannel": "Frissítések keresése",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak követhető appokat ellenőrizze frissítésekért",
|
"onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak követhető appokat ellenőrizze frissítésekért",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Eltávolítja az alkalmazást?",
|
"one": "Eltávolítja az alkalmazást?",
|
||||||
"other": "Eltávolítja az alkalmazást?"
|
"other": "Eltávolítja az alkalmazást?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Non installato",
|
"notInstalled": "Non installato",
|
||||||
"estimateInBrackets": "(stimato)",
|
"estimateInBrackets": "(stimato)",
|
||||||
"selectAll": "Seleziona tutto",
|
"selectAll": "Seleziona tutto",
|
||||||
"deselectN": "Deseleziona {}",
|
"deselectX": "Deseleziona {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "Verà effettuata la rimozione di {}, ma non la disinstallazione.",
|
"xWillBeRemovedButRemainInstalled": "Verà effettuata la rimozione di {}, ma non la disinstallazione.",
|
||||||
"removeSelectedAppsQuestion": "Rimuovere le app selezionate?",
|
"removeSelectedAppsQuestion": "Rimuovere le app selezionate?",
|
||||||
"removeSelectedApps": "Rimuovi le app selezionate",
|
"removeSelectedApps": "Rimuovi le app selezionate",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Sposta le app non installate in fondo alla lista",
|
"moveNonInstalledAppsToBottom": "Sposta le app non installate in fondo alla lista",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(attiva la ricerca e migliora la rilevazione di apk)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(attiva la ricerca e migliora la rilevazione di apk)",
|
||||||
"about": "Informazioni",
|
"about": "Informazioni",
|
||||||
"requiresCredentialsInSettings": "Servono credenziali aggiuntive (in Impostazioni)",
|
"requiresCredentialsInSettings": "{}: Servono credenziali aggiuntive (in Impostazioni)",
|
||||||
"checkOnStart": "Controlla una volta all'avvio",
|
"checkOnStart": "Controlla una volta all'avvio",
|
||||||
"tryInferAppIdFromCode": "Prova a dedurre l'ID dell'app dal codice sorgente",
|
"tryInferAppIdFromCode": "Prova a dedurre l'ID dell'app dal codice sorgente",
|
||||||
"removeOnExternalUninstall": "Rimuovi automaticamente app disinstallate esternamente",
|
"removeOnExternalUninstall": "Rimuovi automaticamente app disinstallate esternamente",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Completa l'installazione dell'app",
|
"completeAppInstallationNotifChannel": "Completa l'installazione dell'app",
|
||||||
"checkingForUpdatesNotifChannel": "Controllo degli aggiornamenti in corso",
|
"checkingForUpdatesNotifChannel": "Controllo degli aggiornamenti in corso",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Cerca aggiornamenti solo per app installate e app in Solo-Monitoraggio",
|
"onlyCheckInstalledOrTrackOnlyApps": "Cerca aggiornamenti solo per app installate e app in Solo-Monitoraggio",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Rimuovere l'app?",
|
"one": "Rimuovere l'app?",
|
||||||
"other": "Rimuovere le app?"
|
"other": "Rimuovere le app?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "未インストール",
|
"notInstalled": "未インストール",
|
||||||
"estimateInBrackets": "(推定)",
|
"estimateInBrackets": "(推定)",
|
||||||
"selectAll": "すべて選択",
|
"selectAll": "すべて選択",
|
||||||
"deselectN": "{}件の選択を解除",
|
"deselectX": "{}件の選択を解除",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} はObtainiumから削除されますが、デバイスにはインストールされたままです。",
|
"xWillBeRemovedButRemainInstalled": "{} はObtainiumから削除されますが、デバイスにはインストールされたままです。",
|
||||||
"removeSelectedAppsQuestion": "選択したアプリを削除しますか?",
|
"removeSelectedAppsQuestion": "選択したアプリを削除しますか?",
|
||||||
"removeSelectedApps": "選択したアプリを削除する",
|
"removeSelectedApps": "選択したアプリを削除する",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "未インストールのアプリをアプリ一覧の下部に移動させる",
|
"moveNonInstalledAppsToBottom": "未インストールのアプリをアプリ一覧の下部に移動させる",
|
||||||
"gitlabPATLabel": "GitLab パーソナルアクセストークン\n(検索とより良いAPK検出の有効化)",
|
"gitlabPATLabel": "GitLab パーソナルアクセストークン\n(検索とより良いAPK検出の有効化)",
|
||||||
"about": "概要",
|
"about": "概要",
|
||||||
"requiresCredentialsInSettings": "これには追加の認証が必要です (設定にて)",
|
"requiresCredentialsInSettings": "{}: これには追加の認証が必要です (設定にて)",
|
||||||
"checkOnStart": "起動時にアップデートを確認する",
|
"checkOnStart": "起動時にアップデートを確認する",
|
||||||
"tryInferAppIdFromCode": "ソースコードからApp IDを推測する",
|
"tryInferAppIdFromCode": "ソースコードからApp IDを推測する",
|
||||||
"removeOnExternalUninstall": "外部でアンインストールされたアプリを自動的に削除する",
|
"removeOnExternalUninstall": "外部でアンインストールされたアプリを自動的に削除する",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "アプリのインストールを完了する",
|
"completeAppInstallationNotifChannel": "アプリのインストールを完了する",
|
||||||
"checkingForUpdatesNotifChannel": "アップデートを確認中",
|
"checkingForUpdatesNotifChannel": "アップデートを確認中",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
|
"onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "アプリを削除しますか?",
|
"one": "アプリを削除しますか?",
|
||||||
"other": "アプリを削除しますか?"
|
"other": "アプリを削除しますか?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Niet geinstalleerd",
|
"notInstalled": "Niet geinstalleerd",
|
||||||
"estimateInBrackets": "(Ongeveer)",
|
"estimateInBrackets": "(Ongeveer)",
|
||||||
"selectAll": "Selecteer alles",
|
"selectAll": "Selecteer alles",
|
||||||
"deselectN": "Deselecteer {}",
|
"deselectX": "Deselecteer {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} zal worden verwijderd uit Obtainium, maar blijft geïnstalleerd op het apparaat.",
|
"xWillBeRemovedButRemainInstalled": "{} zal worden verwijderd uit Obtainium, maar blijft geïnstalleerd op het apparaat.",
|
||||||
"removeSelectedAppsQuestion": "Geselecteerde apps verwijderen??",
|
"removeSelectedAppsQuestion": "Geselecteerde apps verwijderen??",
|
||||||
"removeSelectedApps": "Geselecteerde apps verwijderen",
|
"removeSelectedApps": "Geselecteerde apps verwijderen",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Verplaats niet-geïnstalleerde apps naar de onderkant van de apps-weergave",
|
"moveNonInstalledAppsToBottom": "Verplaats niet-geïnstalleerde apps naar de onderkant van de apps-weergave",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Maakt het mogelijk beter te zoeken naar APK's)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Maakt het mogelijk beter te zoeken naar APK's)",
|
||||||
"about": "Over",
|
"about": "Over",
|
||||||
"requiresCredentialsInSettings": "Dit vereist aanvullende referenties (in Instellingen)",
|
"requiresCredentialsInSettings": "{}: Dit vereist aanvullende referenties (in Instellingen)",
|
||||||
"checkOnStart": "Controleren op updates bij opstarten",
|
"checkOnStart": "Controleren op updates bij opstarten",
|
||||||
"tryInferAppIdFromCode": "Probeer de app-ID af te leiden uit de broncode",
|
"tryInferAppIdFromCode": "Probeer de app-ID af te leiden uit de broncode",
|
||||||
"removeOnExternalUninstall": "Automatisch extern verwijderde apps verwijderen",
|
"removeOnExternalUninstall": "Automatisch extern verwijderde apps verwijderen",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Voltooien van de app-installatie",
|
"completeAppInstallationNotifChannel": "Voltooien van de app-installatie",
|
||||||
"checkingForUpdatesNotifChannel": "Controleren op updates",
|
"checkingForUpdatesNotifChannel": "Controleren op updates",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "App verwijderen?",
|
"one": "App verwijderen?",
|
||||||
"other": "Apps verwijderen?"
|
"other": "Apps verwijderen?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Nie zainstalowano",
|
"notInstalled": "Nie zainstalowano",
|
||||||
"estimateInBrackets": "(Szacunkowo)",
|
"estimateInBrackets": "(Szacunkowo)",
|
||||||
"selectAll": "Zaznacz wszystkie",
|
"selectAll": "Zaznacz wszystkie",
|
||||||
"deselectN": "Odznacz {}",
|
"deselectX": "Odznacz {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} zostanie usunięty z Obtainium, ale pozostanie zainstalowany na urządzeniu.",
|
"xWillBeRemovedButRemainInstalled": "{} zostanie usunięty z Obtainium, ale pozostanie zainstalowany na urządzeniu.",
|
||||||
"removeSelectedAppsQuestion": "Usunąć wybrane aplikacje?",
|
"removeSelectedAppsQuestion": "Usunąć wybrane aplikacje?",
|
||||||
"removeSelectedApps": "Usuń wybrane aplikacje",
|
"removeSelectedApps": "Usuń wybrane aplikacje",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Przenieś niezainstalowane aplikacje na dół widoku aplikacji",
|
"moveNonInstalledAppsToBottom": "Przenieś niezainstalowane aplikacje na dół widoku aplikacji",
|
||||||
"gitlabPATLabel": "Osobisty token dostępu GitLab\n(Umożliwia wyszukiwanie i lepsze wykrywanie APK)",
|
"gitlabPATLabel": "Osobisty token dostępu GitLab\n(Umożliwia wyszukiwanie i lepsze wykrywanie APK)",
|
||||||
"about": "Więcej informacji",
|
"about": "Więcej informacji",
|
||||||
"requiresCredentialsInSettings": "Wymaga to dodatkowych poświadczeń (w Ustawieniach)",
|
"requiresCredentialsInSettings": "{}: Wymaga to dodatkowych poświadczeń (w Ustawieniach)",
|
||||||
"checkOnStart": "Sprawdź aktualizacje przy uruchomieniu",
|
"checkOnStart": "Sprawdź aktualizacje przy uruchomieniu",
|
||||||
"tryInferAppIdFromCode": "Spróbuj wywnioskować identyfikator aplikacji z kodu źródłowego",
|
"tryInferAppIdFromCode": "Spróbuj wywnioskować identyfikator aplikacji z kodu źródłowego",
|
||||||
"removeOnExternalUninstall": "Automatyczne usuń odinstalowane zewnętrznie aplikacje",
|
"removeOnExternalUninstall": "Automatyczne usuń odinstalowane zewnętrznie aplikacje",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Ukończenie instalacji aplikacji",
|
"completeAppInstallationNotifChannel": "Ukończenie instalacji aplikacji",
|
||||||
"checkingForUpdatesNotifChannel": "Sprawdzanie dostępności aktualizacji",
|
"checkingForUpdatesNotifChannel": "Sprawdzanie dostępności aktualizacji",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
|
"onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Usunąć aplikację?",
|
"one": "Usunąć aplikację?",
|
||||||
"few": "Usunąć aplikacje?",
|
"few": "Usunąć aplikacje?",
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Não Instalado",
|
"notInstalled": "Não Instalado",
|
||||||
"estimateInBrackets": "(Aproximado)",
|
"estimateInBrackets": "(Aproximado)",
|
||||||
"selectAll": "Selecionar All",
|
"selectAll": "Selecionar All",
|
||||||
"deselectN": "Deselecionar {}",
|
"deselectX": "Deselecionar {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} sera removido do Obtainium mais permanecerá instalado no dispositivo.",
|
"xWillBeRemovedButRemainInstalled": "{} sera removido do Obtainium mais permanecerá instalado no dispositivo.",
|
||||||
"removeSelectedAppsQuestion": "Remover Apps Selecionados?",
|
"removeSelectedAppsQuestion": "Remover Apps Selecionados?",
|
||||||
"removeSelectedApps": "Remover Apps Selecionados",
|
"removeSelectedApps": "Remover Apps Selecionados",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Mover Apps não instalados para o fundo da visão de Apps",
|
"moveNonInstalledAppsToBottom": "Mover Apps não instalados para o fundo da visão de Apps",
|
||||||
"gitlabPATLabel": "Token de Acceso Pessoal do Gitlab\n(Ativa Pesquisa e Melhor Descoberta de APKs)",
|
"gitlabPATLabel": "Token de Acceso Pessoal do Gitlab\n(Ativa Pesquisa e Melhor Descoberta de APKs)",
|
||||||
"about": "Sobre",
|
"about": "Sobre",
|
||||||
"requiresCredentialsInSettings": "Isso requer credenciais adicionais (em Configurações)",
|
"requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)",
|
||||||
"checkOnStart": "Checar por atualizações ao iniciar ",
|
"checkOnStart": "Checar por atualizações ao iniciar ",
|
||||||
"tryInferAppIdFromCode": "Tente inferir o ID do App pelo código fonte",
|
"tryInferAppIdFromCode": "Tente inferir o ID do App pelo código fonte",
|
||||||
"removeOnExternalUninstall": "Remover automaticamente Apps desinstalados externamente",
|
"removeOnExternalUninstall": "Remover automaticamente Apps desinstalados externamente",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Instalação completa do App",
|
"completeAppInstallationNotifChannel": "Instalação completa do App",
|
||||||
"checkingForUpdatesNotifChannel": "Checando por Atualizações",
|
"checkingForUpdatesNotifChannel": "Checando por Atualizações",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remover App?",
|
"one": "Remover App?",
|
||||||
"other": "Remover Apps?"
|
"other": "Remover Apps?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Не установлено",
|
"notInstalled": "Не установлено",
|
||||||
"estimateInBrackets": "(Оценка)",
|
"estimateInBrackets": "(Оценка)",
|
||||||
"selectAll": "Выбрать всё",
|
"selectAll": "Выбрать всё",
|
||||||
"deselectN": "Отменить выбор {}",
|
"deselectX": "Отменить выбор {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} будет удалено из Obtainium, но останется на устройстве",
|
"xWillBeRemovedButRemainInstalled": "{} будет удалено из Obtainium, но останется на устройстве",
|
||||||
"removeSelectedAppsQuestion": "Удалить выбранные приложения?",
|
"removeSelectedAppsQuestion": "Удалить выбранные приложения?",
|
||||||
"removeSelectedApps": "Удалить выбранные приложения",
|
"removeSelectedApps": "Удалить выбранные приложения",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка",
|
"moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка",
|
||||||
"gitlabPATLabel": "Персональный токен доступа GitLab\n(включает поиск и улучшает обнаружение APK)",
|
"gitlabPATLabel": "Персональный токен доступа GitLab\n(включает поиск и улучшает обнаружение APK)",
|
||||||
"about": "Описание",
|
"about": "Описание",
|
||||||
"requiresCredentialsInSettings": "Для этого требуются дополнительные учетные данные (в настройках)",
|
"requiresCredentialsInSettings": "{}: Для этого требуются дополнительные учетные данные (в настройках)",
|
||||||
"checkOnStart": "Проверять наличие обновлений при запуске",
|
"checkOnStart": "Проверять наличие обновлений при запуске",
|
||||||
"tryInferAppIdFromCode": "Попытаться определить ID приложения из исходного кода",
|
"tryInferAppIdFromCode": "Попытаться определить ID приложения из исходного кода",
|
||||||
"removeOnExternalUninstall": "Автоматически убирать из списка удаленные извне приложения",
|
"removeOnExternalUninstall": "Автоматически убирать из списка удаленные извне приложения",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Завершение установки приложения",
|
"completeAppInstallationNotifChannel": "Завершение установки приложения",
|
||||||
"checkingForUpdatesNotifChannel": "Проверка обновлений",
|
"checkingForUpdatesNotifChannel": "Проверка обновлений",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Удалить приложение?",
|
"one": "Удалить приложение?",
|
||||||
"other": "Удалить приложения?"
|
"other": "Удалить приложения?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Inte Installerad",
|
"notInstalled": "Inte Installerad",
|
||||||
"estimateInBrackets": "(Uppskattning)",
|
"estimateInBrackets": "(Uppskattning)",
|
||||||
"selectAll": "Välj Alla",
|
"selectAll": "Välj Alla",
|
||||||
"deselectN": "Avmarkera {}",
|
"deselectX": "Avmarkera {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} kommer tas bort från Obtainium men kommer vara fortsatt installerad på enheten.",
|
"xWillBeRemovedButRemainInstalled": "{} kommer tas bort från Obtainium men kommer vara fortsatt installerad på enheten.",
|
||||||
"removeSelectedAppsQuestion": "Ta bort markerade Appar?",
|
"removeSelectedAppsQuestion": "Ta bort markerade Appar?",
|
||||||
"removeSelectedApps": "Ta bort markerade Appar",
|
"removeSelectedApps": "Ta bort markerade Appar",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
||||||
"about": "Om",
|
"about": "Om",
|
||||||
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
|
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
|
||||||
"checkOnStart": "Kolla efter uppdateringar vid start",
|
"checkOnStart": "Kolla efter uppdateringar vid start",
|
||||||
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
||||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Yüklenmedi",
|
"notInstalled": "Yüklenmedi",
|
||||||
"estimateInBrackets": "(Tahmini)",
|
"estimateInBrackets": "(Tahmini)",
|
||||||
"selectAll": "Hepsini Seç",
|
"selectAll": "Hepsini Seç",
|
||||||
"deselectN": "{}'yi Seçimden Kaldır",
|
"deselectX": "{}'yi Seçimden Kaldır",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} Obtainium'dan kaldırılacak ancak cihazınızda yüklü kalacaktır.",
|
"xWillBeRemovedButRemainInstalled": "{} Obtainium'dan kaldırılacak ancak cihazınızda yüklü kalacaktır.",
|
||||||
"removeSelectedAppsQuestion": "Seçilen Uygulamaları Kaldırmak İstiyor musunuz?",
|
"removeSelectedAppsQuestion": "Seçilen Uygulamaları Kaldırmak İstiyor musunuz?",
|
||||||
"removeSelectedApps": "Seçilen Uygulamaları Kaldır",
|
"removeSelectedApps": "Seçilen Uygulamaları Kaldır",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Yüklenmemiş Uygulamaları Uygulamalar Görünümünün Altına Taşı",
|
"moveNonInstalledAppsToBottom": "Yüklenmemiş Uygulamaları Uygulamalar Görünümünün Altına Taşı",
|
||||||
"gitlabPATLabel": "GitLab Kişisel Erişim Belirteci\n(Arama ve Daha İyi APK Keşfi İçin)",
|
"gitlabPATLabel": "GitLab Kişisel Erişim Belirteci\n(Arama ve Daha İyi APK Keşfi İçin)",
|
||||||
"about": "Hakkında",
|
"about": "Hakkında",
|
||||||
"requiresCredentialsInSettings": "Bu, ek kimlik bilgilerine ihtiyaç duyar (Ayarlar'da)",
|
"requiresCredentialsInSettings": "{}: Bu, ek kimlik bilgilerine ihtiyaç duyar (Ayarlar'da)",
|
||||||
"checkOnStart": "Başlangıçta güncellemeleri kontrol et",
|
"checkOnStart": "Başlangıçta güncellemeleri kontrol et",
|
||||||
"tryInferAppIdFromCode": "Uygulama kimliğini kaynak kodundan çıkarma girişimi",
|
"tryInferAppIdFromCode": "Uygulama kimliğini kaynak kodundan çıkarma girişimi",
|
||||||
"removeOnExternalUninstall": "Harici kaldırmada otomatik olarak kaldırılan uygulamalar",
|
"removeOnExternalUninstall": "Harici kaldırmada otomatik olarak kaldırılan uygulamalar",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Uygulama Kurulumu Tamamlandı",
|
"completeAppInstallationNotifChannel": "Uygulama Kurulumu Tamamlandı",
|
||||||
"checkingForUpdatesNotifChannel": "Güncellemeler Kontrol Ediliyor",
|
"checkingForUpdatesNotifChannel": "Güncellemeler Kontrol Ediliyor",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
|
"onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Uygulamayı Kaldır?",
|
"one": "Uygulamayı Kaldır?",
|
||||||
"other": "Uygulamaları Kaldır?"
|
"other": "Uygulamaları Kaldır?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "Chưa cài đặt",
|
"notInstalled": "Chưa cài đặt",
|
||||||
"estimateInBrackets": "(Ước lượng)",
|
"estimateInBrackets": "(Ước lượng)",
|
||||||
"selectAll": "Chọn tất cả",
|
"selectAll": "Chọn tất cả",
|
||||||
"deselectN": "Bỏ chọn {}",
|
"deselectX": "Bỏ chọn {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} sẽ bị xóa khỏi Obtainium nhưng vẫn còn cài đặt trên thiết bị.",
|
"xWillBeRemovedButRemainInstalled": "{} sẽ bị xóa khỏi Obtainium nhưng vẫn còn cài đặt trên thiết bị.",
|
||||||
"removeSelectedAppsQuestion": "Xóa ứng dụng đã chọn?",
|
"removeSelectedAppsQuestion": "Xóa ứng dụng đã chọn?",
|
||||||
"removeSelectedApps": "Xóa ứng dụng đã chọn",
|
"removeSelectedApps": "Xóa ứng dụng đã chọn",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Di chuyển Ứng dụng chưa được cài đặt xuống cuối chế độ xem Ứng dụng",
|
"moveNonInstalledAppsToBottom": "Di chuyển Ứng dụng chưa được cài đặt xuống cuối chế độ xem Ứng dụng",
|
||||||
"gitlabPATLabel": "Mã thông báo truy cập cá nhân GitLab\n(Cho phép tìm kiếm và khám phá APK tốt hơn)",
|
"gitlabPATLabel": "Mã thông báo truy cập cá nhân GitLab\n(Cho phép tìm kiếm và khám phá APK tốt hơn)",
|
||||||
"about": "Giới thiệu",
|
"about": "Giới thiệu",
|
||||||
"requiresCredentialsInSettings": "Điều này cần thông tin xác thực bổ sung (trong Cài đặt)",
|
"requiresCredentialsInSettings": "{}: Điều này cần thông tin xác thực bổ sung (trong Cài đặt)",
|
||||||
"checkOnStart": "Kiểm tra các bản cập nhật khi khởi động",
|
"checkOnStart": "Kiểm tra các bản cập nhật khi khởi động",
|
||||||
"tryInferAppIdFromCode": "Thử suy ra ID ứng dụng từ mã nguồn",
|
"tryInferAppIdFromCode": "Thử suy ra ID ứng dụng từ mã nguồn",
|
||||||
"removeOnExternalUninstall": "Tự động xóa ứng dụng đã gỡ cài đặt bên ngoài",
|
"removeOnExternalUninstall": "Tự động xóa ứng dụng đã gỡ cài đặt bên ngoài",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "Hoàn tất cài đặt ứng dụng",
|
"completeAppInstallationNotifChannel": "Hoàn tất cài đặt ứng dụng",
|
||||||
"checkingForUpdatesNotifChannel": "Đang kiểm tra cập nhật",
|
"checkingForUpdatesNotifChannel": "Đang kiểm tra cập nhật",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra các ứng dụng đã cài đặt và Chỉ-Theo dõi để biết các bản cập nhật",
|
"onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra các ứng dụng đã cài đặt và Chỉ-Theo dõi để biết các bản cập nhật",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion":{
|
"removeAppQuestion":{
|
||||||
"one": "Gỡ ứng dụng?",
|
"one": "Gỡ ứng dụng?",
|
||||||
"other": "Gỡ ứng dụng?"
|
"other": "Gỡ ứng dụng?"
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
"notInstalled": "未安装",
|
"notInstalled": "未安装",
|
||||||
"estimateInBrackets": "(推测)",
|
"estimateInBrackets": "(推测)",
|
||||||
"selectAll": "全选",
|
"selectAll": "全选",
|
||||||
"deselectN": "取消选择 {}",
|
"deselectX": "取消选择 {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} 将从 Obtainium 中删除,但仍安装在您的设备中。",
|
"xWillBeRemovedButRemainInstalled": "{} 将从 Obtainium 中删除,但仍安装在您的设备中。",
|
||||||
"removeSelectedAppsQuestion": "是否删除选中的应用?",
|
"removeSelectedAppsQuestion": "是否删除选中的应用?",
|
||||||
"removeSelectedApps": "删除选中的应用",
|
"removeSelectedApps": "删除选中的应用",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "将未安装应用置底",
|
"moveNonInstalledAppsToBottom": "将未安装应用置底",
|
||||||
"gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)",
|
"gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)",
|
||||||
"about": "相关文档",
|
"about": "相关文档",
|
||||||
"requiresCredentialsInSettings": "此功能需要额外的凭据(在“设置”中添加)",
|
"requiresCredentialsInSettings": "{}: 此功能需要额外的凭据(在“设置”中添加)",
|
||||||
"checkOnStart": "启动时进行一次检查",
|
"checkOnStart": "启动时进行一次检查",
|
||||||
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
|
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
|
||||||
"removeOnExternalUninstall": "自动删除已卸载的外部应用",
|
"removeOnExternalUninstall": "自动删除已卸载的外部应用",
|
||||||
@@ -275,6 +275,8 @@
|
|||||||
"completeAppInstallationNotifChannel": "完成应用安装",
|
"completeAppInstallationNotifChannel": "完成应用安装",
|
||||||
"checkingForUpdatesNotifChannel": "正在检查更新",
|
"checkingForUpdatesNotifChannel": "正在检查更新",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "只对已安装和“仅追踪”的应用进行更新检查",
|
"onlyCheckInstalledOrTrackOnlyApps": "只对已安装和“仅追踪”的应用进行更新检查",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "是否删除应用?",
|
"one": "是否删除应用?",
|
||||||
"other": "是否删除应用?"
|
"other": "是否删除应用?"
|
||||||
|
@@ -32,7 +32,8 @@ class APKMirror extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/apk/[^/]+/[^/]+');
|
RegExp standardUrlRegEx =
|
||||||
|
RegExp('^https?://(www\\.)?$host/apk/[^/]+/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@@ -139,11 +139,11 @@ APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Apply the release filter if any
|
// Apply the release filter if any
|
||||||
if (filterVersionsByRegEx != null) {
|
if (filterVersionsByRegEx?.isNotEmpty == true) {
|
||||||
version = null;
|
version = null;
|
||||||
releaseChoices = [];
|
releaseChoices = [];
|
||||||
for (var i = 0; i < releases.length; i++) {
|
for (var i = 0; i < releases.length; i++) {
|
||||||
if (RegExp(filterVersionsByRegEx)
|
if (RegExp(filterVersionsByRegEx!)
|
||||||
.hasMatch(releases[i]['versionName'])) {
|
.hasMatch(releases[i]['versionName'])) {
|
||||||
version = releases[i]['versionName'];
|
version = releases[i]['versionName'];
|
||||||
}
|
}
|
||||||
|
@@ -54,17 +54,25 @@ class FDroidRepo extends AppSource {
|
|||||||
@override
|
@override
|
||||||
Future<Map<String, List<String>>> search(String query,
|
Future<Map<String, List<String>>> search(String query,
|
||||||
{Map<String, dynamic> querySettings = const {}}) async {
|
{Map<String, dynamic> querySettings = const {}}) async {
|
||||||
query = removeQueryParamsFromUrl(standardizeUrl(query));
|
String? url = querySettings['url'];
|
||||||
var res = await sourceRequest('$query/index.xml');
|
if (url == null) {
|
||||||
|
throw NoReleasesError();
|
||||||
|
}
|
||||||
|
url = removeQueryParamsFromUrl(standardizeUrl(url));
|
||||||
|
var res = await sourceRequest('$url/index.xml');
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var body = parse(res.body);
|
var body = parse(res.body);
|
||||||
Map<String, List<String>> results = {};
|
Map<String, List<String>> results = {};
|
||||||
body.querySelectorAll('application').toList().forEach((app) {
|
body.querySelectorAll('application').toList().forEach((app) {
|
||||||
String appId = app.attributes['id']!;
|
String appId = app.attributes['id']!;
|
||||||
results['$query?appId=$appId'] = [
|
String appName = app.querySelector('name')?.innerHtml ?? appId;
|
||||||
app.querySelector('name')?.innerHtml ?? appId,
|
String appDesc = app.querySelector('desc')?.innerHtml ?? '';
|
||||||
app.querySelector('desc')?.innerHtml ?? ''
|
if (query.isEmpty ||
|
||||||
];
|
appId.contains(query) ||
|
||||||
|
appName.contains(query) ||
|
||||||
|
appDesc.contains(query)) {
|
||||||
|
results['$url?appId=$appId'] = [appName, appDesc];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return results;
|
return results;
|
||||||
} else {
|
} else {
|
||||||
|
@@ -117,22 +117,23 @@ class GitHub extends AppSource {
|
|||||||
.decode(body['content'].toString().split('\n').join('')))
|
.decode(body['content'].toString().split('\n').join('')))
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map((e) => e.trim());
|
.map((e) => e.trim());
|
||||||
var appId = trimmedLines
|
var appIds = trimmedLines.where((l) =>
|
||||||
.where((l) =>
|
l.startsWith('applicationId "') ||
|
||||||
l.startsWith('applicationId "') ||
|
l.startsWith('applicationId \''));
|
||||||
l.startsWith('applicationId \''))
|
appIds = appIds.map((appId) => appId
|
||||||
.first;
|
.split(appId.startsWith('applicationId "') ? '"' : '\'')[1]);
|
||||||
appId = appId
|
appIds = appIds.map((appId) {
|
||||||
.split(appId.startsWith('applicationId "') ? '"' : '\'')[1];
|
if (appId.startsWith('\${') && appId.endsWith('}')) {
|
||||||
if (appId.startsWith('\${') && appId.endsWith('}')) {
|
appId = trimmedLines
|
||||||
appId = trimmedLines
|
.where((l) => l.startsWith(
|
||||||
.where((l) => l.startsWith(
|
'def ${appId.substring(2, appId.length - 1)}'))
|
||||||
'def ${appId.substring(2, appId.length - 1)}'))
|
.first;
|
||||||
.first;
|
appId = appId.split(appId.contains('"') ? '"' : '\'')[1];
|
||||||
appId = appId.split(appId.contains('"') ? '"' : '\'')[1];
|
}
|
||||||
}
|
|
||||||
if (appId.isNotEmpty) {
|
|
||||||
return appId;
|
return appId;
|
||||||
|
}).where((appId) => appId.isNotEmpty);
|
||||||
|
if (appIds.length == 1) {
|
||||||
|
return appIds.first;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
LogsProvider().add(
|
LogsProvider().add(
|
||||||
|
@@ -48,6 +48,12 @@ class GitLab extends AppSource {
|
|||||||
label: tr('fallbackToOlderReleases'), defaultValue: true)
|
label: tr('fallbackToOlderReleases'), defaultValue: true)
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
searchQuerySettingFormItems = [
|
||||||
|
GeneratedFormTextField('PAT',
|
||||||
|
label: tr('gitlabPATLabel').split('(')[0],
|
||||||
|
password: true,
|
||||||
|
required: false)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -80,12 +86,18 @@ class GitLab extends AppSource {
|
|||||||
@override
|
@override
|
||||||
Future<Map<String, List<String>>> search(String query,
|
Future<Map<String, List<String>>> search(String query,
|
||||||
{Map<String, dynamic> querySettings = const {}}) async {
|
{Map<String, dynamic> querySettings = const {}}) async {
|
||||||
String? PAT = await getPATIfAny({});
|
String? PAT;
|
||||||
if (PAT == null) {
|
if (!hostChanged) {
|
||||||
throw CredsNeededError(name);
|
PAT = await getPATIfAny({});
|
||||||
|
if (PAT == null) {
|
||||||
|
throw CredsNeededError(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((querySettings['PAT'] as String?)?.isNotEmpty == true) {
|
||||||
|
PAT = querySettings['PAT'];
|
||||||
}
|
}
|
||||||
var url =
|
var url =
|
||||||
'https://$host/api/v4/search?private_token=$PAT&scope=projects&search=${Uri.encodeQueryComponent(query)}';
|
'https://$host/api/v4/search?${PAT?.isNotEmpty == true ? 'private_token=$PAT&' : ''}scope=projects&search=${Uri.encodeQueryComponent(query)}';
|
||||||
var res = await sourceRequest(url);
|
var res = await sourceRequest(url);
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
@@ -174,7 +186,6 @@ class GitLab extends AppSource {
|
|||||||
...getLinksFromParsedHTML(entryContent,
|
...getLinksFromParsedHTML(entryContent,
|
||||||
RegExp('/[^/]+\\.apk\$', caseSensitive: false), '')
|
RegExp('/[^/]+\\.apk\$', caseSensitive: false), '')
|
||||||
.where((element) => Uri.parse(element).host != '')
|
.where((element) => Uri.parse(element).host != '')
|
||||||
|
|
||||||
];
|
];
|
||||||
var entryId = entry.querySelector('id')?.innerHtml;
|
var entryId = entry.querySelector('id')?.innerHtml;
|
||||||
var version =
|
var version =
|
||||||
|
@@ -4,6 +4,7 @@ import 'package:html/parser.dart';
|
|||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:obtainium/components/generated_form.dart';
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
|
import 'package:obtainium/providers/apps_provider.dart';
|
||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
|
String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
|
||||||
@@ -94,6 +95,10 @@ class HTML extends AppSource {
|
|||||||
label: tr('sortByFileNamesNotLinks'))
|
label: tr('sortByFileNamesNotLinks'))
|
||||||
],
|
],
|
||||||
[GeneratedFormSwitch('reverseSort', label: tr('reverseSort'))],
|
[GeneratedFormSwitch('reverseSort', label: tr('reverseSort'))],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('supportFixedAPKURL',
|
||||||
|
defaultValue: true, label: tr('supportFixedAPKURL')),
|
||||||
|
],
|
||||||
[
|
[
|
||||||
GeneratedFormTextField('customLinkFilterRegex',
|
GeneratedFormTextField('customLinkFilterRegex',
|
||||||
label: tr('customLinkFilterRegex'),
|
label: tr('customLinkFilterRegex'),
|
||||||
@@ -222,7 +227,10 @@ class HTML extends AppSource {
|
|||||||
throw NoReleasesError();
|
throw NoReleasesError();
|
||||||
}
|
}
|
||||||
var rel = links.last;
|
var rel = links.last;
|
||||||
String? version = rel.hashCode.toString();
|
String? version;
|
||||||
|
if (additionalSettings['supportFixedAPKURL'] != true) {
|
||||||
|
version = rel.hashCode.toString();
|
||||||
|
}
|
||||||
var versionExtractionRegEx =
|
var versionExtractionRegEx =
|
||||||
additionalSettings['versionExtractionRegEx'] as String?;
|
additionalSettings['versionExtractionRegEx'] as String?;
|
||||||
if (versionExtractionRegEx?.isNotEmpty == true) {
|
if (versionExtractionRegEx?.isNotEmpty == true) {
|
||||||
@@ -243,9 +251,9 @@ class HTML extends AppSource {
|
|||||||
throw NoVersionError();
|
throw NoVersionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<String> apkUrls =
|
rel = ensureAbsoluteUrl(rel, uri);
|
||||||
[rel].map((e) => ensureAbsoluteUrl(e, uri)).toList();
|
version ??= (await checkDownloadHash(rel)).toString();
|
||||||
return APKDetails(version!, apkUrls.map((e) => MapEntry(e, e)).toList(),
|
return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(),
|
||||||
AppNames(uri.host, tr('app')));
|
AppNames(uri.host, tr('app')));
|
||||||
} else {
|
} else {
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
|
@@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
|
|||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:easy_localization/src/localization.dart';
|
import 'package:easy_localization/src/localization.dart';
|
||||||
|
|
||||||
const String currentVersion = '0.14.34';
|
const String currentVersion = '0.14.35';
|
||||||
const String currentReleaseTag =
|
const String currentReleaseTag =
|
||||||
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||||
|
|
||||||
|
@@ -254,57 +254,79 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
runSearch() async {
|
runSearch({bool filtered = true}) async {
|
||||||
setState(() {
|
setState(() {
|
||||||
searching = true;
|
searching = true;
|
||||||
});
|
});
|
||||||
|
var sourceStrings = <String, List<String>>{};
|
||||||
|
sourceProvider.sources
|
||||||
|
.where((e) => e.canSearch && !e.excludeFromMassSearch)
|
||||||
|
.forEach((s) {
|
||||||
|
sourceStrings[s.name] = [s.name];
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
var results = await Future.wait(sourceProvider.sources
|
var searchSources = await showDialog<List<String>?>(
|
||||||
.where((e) => e.canSearch && !e.excludeFromMassSearch)
|
|
||||||
.map((e) async {
|
|
||||||
try {
|
|
||||||
return await e.search(searchQuery);
|
|
||||||
} catch (err) {
|
|
||||||
if (err is! CredsNeededError) {
|
|
||||||
rethrow;
|
|
||||||
} else {
|
|
||||||
return <String, List<String>>{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// .then((results) async {
|
|
||||||
// Interleave results instead of simple reduce
|
|
||||||
Map<String, List<String>> res = {};
|
|
||||||
var si = 0;
|
|
||||||
var done = false;
|
|
||||||
while (!done) {
|
|
||||||
done = true;
|
|
||||||
for (var r in results) {
|
|
||||||
if (r.length > si) {
|
|
||||||
done = false;
|
|
||||||
res.addEntries([r.entries.elementAt(si)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
si++;
|
|
||||||
}
|
|
||||||
if (res.isEmpty) {
|
|
||||||
throw ObtainiumError(tr('noResults'));
|
|
||||||
}
|
|
||||||
List<String>? selectedUrls = res.isEmpty
|
|
||||||
? []
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
: await showDialog<List<String>?>(
|
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext ctx) {
|
builder: (BuildContext ctx) {
|
||||||
return UrlSelectionModal(
|
return SelectionModal(
|
||||||
urlsWithDescriptions: res,
|
title: tr('selectX', args: [plural('source', 2)]),
|
||||||
selectedByDefault: false,
|
entries: sourceStrings,
|
||||||
onlyOneSelectionAllowed: true,
|
selectedByDefault: true,
|
||||||
|
onlyOneSelectionAllowed: false,
|
||||||
|
titlesAreLinks: false,
|
||||||
);
|
);
|
||||||
});
|
}) ??
|
||||||
if (selectedUrls != null && selectedUrls.isNotEmpty) {
|
[];
|
||||||
changeUserInput(selectedUrls[0], true, false, isSearch: true);
|
if (searchSources.isNotEmpty) {
|
||||||
|
var results = await Future.wait(sourceProvider.sources
|
||||||
|
.where((e) => searchSources.contains(e.name))
|
||||||
|
.map((e) async {
|
||||||
|
try {
|
||||||
|
return await e.search(searchQuery);
|
||||||
|
} catch (err) {
|
||||||
|
if (err is! CredsNeededError) {
|
||||||
|
rethrow;
|
||||||
|
} else {
|
||||||
|
err.unexpected = true;
|
||||||
|
showError(err, context);
|
||||||
|
return <String, List<String>>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// .then((results) async {
|
||||||
|
// Interleave results instead of simple reduce
|
||||||
|
Map<String, List<String>> res = {};
|
||||||
|
var si = 0;
|
||||||
|
var done = false;
|
||||||
|
while (!done) {
|
||||||
|
done = true;
|
||||||
|
for (var r in results) {
|
||||||
|
if (r.length > si) {
|
||||||
|
done = false;
|
||||||
|
res.addEntries([r.entries.elementAt(si)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
si++;
|
||||||
|
}
|
||||||
|
if (res.isEmpty) {
|
||||||
|
throw ObtainiumError(tr('noResults'));
|
||||||
|
}
|
||||||
|
List<String>? selectedUrls = res.isEmpty
|
||||||
|
? []
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
: await showDialog<List<String>?>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return SelectionModal(
|
||||||
|
entries: res,
|
||||||
|
selectedByDefault: false,
|
||||||
|
onlyOneSelectionAllowed: true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (selectedUrls != null && selectedUrls.isNotEmpty) {
|
||||||
|
changeUserInput(selectedUrls[0], true, false, isSearch: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showError(e, context);
|
showError(e, context);
|
||||||
@@ -470,23 +492,21 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 16,
|
height: 16,
|
||||||
),
|
),
|
||||||
...sourceProvider.sources
|
...sourceProvider.sources.map((e) => GestureDetector(
|
||||||
.map((e) => GestureDetector(
|
onTap: e.host != null
|
||||||
onTap: e.host != null
|
? () {
|
||||||
? () {
|
launchUrlString('https://${e.host}',
|
||||||
launchUrlString('https://${e.host}',
|
mode: LaunchMode.externalApplication);
|
||||||
mode: LaunchMode.externalApplication);
|
}
|
||||||
}
|
: null,
|
||||||
: null,
|
child: Text(
|
||||||
child: Text(
|
'${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}',
|
||||||
'${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}',
|
style: TextStyle(
|
||||||
style: TextStyle(
|
decoration: e.host != null
|
||||||
decoration: e.host != null
|
? TextDecoration.underline
|
||||||
? TextDecoration.underline
|
: TextDecoration.none,
|
||||||
: TextDecoration.none,
|
fontStyle: FontStyle.italic),
|
||||||
fontStyle: FontStyle.italic),
|
)))
|
||||||
)))
|
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@@ -4,6 +4,7 @@ import 'dart:io';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:obtainium/app_sources/fdroidrepo.dart';
|
||||||
import 'package:obtainium/components/custom_app_bar.dart';
|
import 'package:obtainium/components/custom_app_bar.dart';
|
||||||
import 'package:obtainium/components/generated_form.dart';
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
import 'package:obtainium/components/generated_form_modal.dart';
|
import 'package:obtainium/components/generated_form_modal.dart';
|
||||||
@@ -189,17 +190,29 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
items: [
|
items: [
|
||||||
[
|
[
|
||||||
GeneratedFormTextField('searchQuery',
|
GeneratedFormTextField('searchQuery',
|
||||||
label: tr('searchQuery'))
|
label: tr('searchQuery'),
|
||||||
|
required: source.name != FDroidRepo().name)
|
||||||
|
],
|
||||||
|
...source.searchQuerySettingFormItems.map((e) => [e]),
|
||||||
|
[
|
||||||
|
GeneratedFormTextField('url',
|
||||||
|
label: source.host != null
|
||||||
|
? tr('overrideSource')
|
||||||
|
: plural('url', 1).substring(2),
|
||||||
|
defaultValue: source.host ?? '',
|
||||||
|
required: true)
|
||||||
],
|
],
|
||||||
...source.searchQuerySettingFormItems.map((e) => [e])
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (values != null &&
|
if (values != null) {
|
||||||
(values['searchQuery'] as String?)?.isNotEmpty == true) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
importInProgress = true;
|
importInProgress = true;
|
||||||
});
|
});
|
||||||
|
if (values['url'] != source.host) {
|
||||||
|
source = sourceProvider.getSource(values['url'],
|
||||||
|
overrideSource: source.runtimeType.toString());
|
||||||
|
}
|
||||||
var urlsWithDescriptions = await source
|
var urlsWithDescriptions = await source
|
||||||
.search(values['searchQuery'] as String, querySettings: values);
|
.search(values['searchQuery'] as String, querySettings: values);
|
||||||
if (urlsWithDescriptions.isNotEmpty) {
|
if (urlsWithDescriptions.isNotEmpty) {
|
||||||
@@ -208,8 +221,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
await showDialog<List<String>?>(
|
await showDialog<List<String>?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext ctx) {
|
builder: (BuildContext ctx) {
|
||||||
return UrlSelectionModal(
|
return SelectionModal(
|
||||||
urlsWithDescriptions: urlsWithDescriptions,
|
entries: urlsWithDescriptions,
|
||||||
selectedByDefault: false,
|
selectedByDefault: false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -269,8 +282,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
await showDialog<List<String>?>(
|
await showDialog<List<String>?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext ctx) {
|
builder: (BuildContext ctx) {
|
||||||
return UrlSelectionModal(
|
return SelectionModal(entries: urlsWithDescriptions);
|
||||||
urlsWithDescriptions: urlsWithDescriptions);
|
|
||||||
});
|
});
|
||||||
if (selectedUrls != null) {
|
if (selectedUrls != null) {
|
||||||
var errors = await appsProvider.addAppsByURL(selectedUrls);
|
var errors = await appsProvider.addAppsByURL(selectedUrls);
|
||||||
@@ -300,6 +312,11 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sourceStrings = <String, List<String>>{};
|
||||||
|
sourceProvider.sources.where((e) => e.canSearch).forEach((s) {
|
||||||
|
sourceStrings[s.name] = [s.name];
|
||||||
|
});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
body: CustomScrollView(slivers: <Widget>[
|
body: CustomScrollView(slivers: <Widget>[
|
||||||
@@ -409,6 +426,54 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
const Divider(
|
const Divider(
|
||||||
height: 32,
|
height: 32,
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: importInProgress
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
var searchSourceName =
|
||||||
|
await showDialog<
|
||||||
|
List<String>?>(
|
||||||
|
context: context,
|
||||||
|
builder:
|
||||||
|
(BuildContext
|
||||||
|
ctx) {
|
||||||
|
return SelectionModal(
|
||||||
|
title: tr(
|
||||||
|
'selectX',
|
||||||
|
args: [
|
||||||
|
tr('source')
|
||||||
|
]),
|
||||||
|
entries:
|
||||||
|
sourceStrings,
|
||||||
|
selectedByDefault:
|
||||||
|
false,
|
||||||
|
onlyOneSelectionAllowed:
|
||||||
|
true,
|
||||||
|
titlesAreLinks:
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}) ??
|
||||||
|
[];
|
||||||
|
var searchSource =
|
||||||
|
sourceProvider.sources
|
||||||
|
.where((e) =>
|
||||||
|
searchSourceName
|
||||||
|
.contains(
|
||||||
|
e.name))
|
||||||
|
.toList();
|
||||||
|
if (searchSource.isNotEmpty) {
|
||||||
|
runSourceSearch(
|
||||||
|
searchSource[0]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(tr('searchX',
|
||||||
|
args: [tr('source')])))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
importInProgress ? null : urlListImport,
|
importInProgress ? null : urlListImport,
|
||||||
@@ -424,39 +489,19 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
...sourceProvider.sources
|
...sourceProvider.massUrlSources.map((source) => Column(
|
||||||
.where((element) => element.canSearch)
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
.map((source) => Column(
|
children: [
|
||||||
crossAxisAlignment:
|
const SizedBox(height: 8),
|
||||||
CrossAxisAlignment.stretch,
|
TextButton(
|
||||||
children: [
|
onPressed: importInProgress
|
||||||
const SizedBox(height: 8),
|
? null
|
||||||
TextButton(
|
: () {
|
||||||
onPressed: importInProgress
|
runMassSourceImport(source);
|
||||||
? null
|
},
|
||||||
: () {
|
child: Text(
|
||||||
runSourceSearch(source);
|
tr('importX', args: [source.name])))
|
||||||
},
|
])),
|
||||||
child: Text(
|
|
||||||
tr('searchX', args: [source.name])))
|
|
||||||
]))
|
|
||||||
,
|
|
||||||
...sourceProvider.massUrlSources
|
|
||||||
.map((source) => Column(
|
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextButton(
|
|
||||||
onPressed: importInProgress
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
runMassSourceImport(source);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
tr('importX', args: [source.name])))
|
|
||||||
]))
|
|
||||||
,
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
const Divider(
|
const Divider(
|
||||||
height: 32,
|
height: 32,
|
||||||
@@ -532,38 +577,42 @@ class _ImportErrorDialogState extends State<ImportErrorDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class UrlSelectionModal extends StatefulWidget {
|
class SelectionModal extends StatefulWidget {
|
||||||
UrlSelectionModal(
|
SelectionModal(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.urlsWithDescriptions,
|
required this.entries,
|
||||||
this.selectedByDefault = true,
|
this.selectedByDefault = true,
|
||||||
this.onlyOneSelectionAllowed = false});
|
this.onlyOneSelectionAllowed = false,
|
||||||
|
this.titlesAreLinks = true,
|
||||||
|
this.title});
|
||||||
|
|
||||||
Map<String, List<String>> urlsWithDescriptions;
|
String? title;
|
||||||
|
Map<String, List<String>> entries;
|
||||||
bool selectedByDefault;
|
bool selectedByDefault;
|
||||||
bool onlyOneSelectionAllowed;
|
bool onlyOneSelectionAllowed;
|
||||||
|
bool titlesAreLinks;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<UrlSelectionModal> createState() => _UrlSelectionModalState();
|
State<SelectionModal> createState() => _SelectionModalState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
class _SelectionModalState extends State<SelectionModal> {
|
||||||
Map<MapEntry<String, List<String>>, bool> urlWithDescriptionSelections = {};
|
Map<MapEntry<String, List<String>>, bool> entrySelections = {};
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
for (var url in widget.urlsWithDescriptions.entries) {
|
for (var url in widget.entries.entries) {
|
||||||
urlWithDescriptionSelections.putIfAbsent(url,
|
entrySelections.putIfAbsent(url,
|
||||||
() => widget.selectedByDefault && !widget.onlyOneSelectionAllowed);
|
() => widget.selectedByDefault && !widget.onlyOneSelectionAllowed);
|
||||||
}
|
}
|
||||||
if (widget.selectedByDefault && widget.onlyOneSelectionAllowed) {
|
if (widget.selectedByDefault && widget.onlyOneSelectionAllowed) {
|
||||||
selectOnlyOne(widget.urlsWithDescriptions.entries.first.key);
|
selectOnlyOne(widget.entries.entries.first.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectOnlyOne(String url) {
|
selectOnlyOne(String url) {
|
||||||
for (var uwd in urlWithDescriptionSelections.keys) {
|
for (var e in entrySelections.keys) {
|
||||||
urlWithDescriptionSelections[uwd] = uwd.key == url;
|
entrySelections[e] = e.key == url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,73 +620,88 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
scrollable: true,
|
scrollable: true,
|
||||||
title: Text(
|
title: Text(widget.title ?? tr('pick')),
|
||||||
widget.onlyOneSelectionAllowed ? tr('selectURL') : tr('selectURLs')),
|
|
||||||
content: Column(children: [
|
content: Column(children: [
|
||||||
...urlWithDescriptionSelections.keys.map((urlWithD) {
|
...entrySelections.keys.map((entry) {
|
||||||
selectThis(bool? value) {
|
selectThis(bool? value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
value ??= false;
|
value ??= false;
|
||||||
if (value! && widget.onlyOneSelectionAllowed) {
|
if (value! && widget.onlyOneSelectionAllowed) {
|
||||||
selectOnlyOne(urlWithD.key);
|
selectOnlyOne(entry.key);
|
||||||
} else {
|
} else {
|
||||||
urlWithDescriptionSelections[urlWithD] = value!;
|
entrySelections[entry] = value!;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var urlLink = GestureDetector(
|
var urlLink = GestureDetector(
|
||||||
onTap: () {
|
onTap: !widget.titlesAreLinks
|
||||||
launchUrlString(urlWithD.key,
|
? null
|
||||||
mode: LaunchMode.externalApplication);
|
: () {
|
||||||
},
|
launchUrlString(entry.key,
|
||||||
|
mode: LaunchMode.externalApplication);
|
||||||
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
urlWithD.value[0],
|
entry.value.isEmpty ? entry.key : entry.value[0],
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: widget.titlesAreLinks
|
||||||
|
? TextDecoration.underline
|
||||||
|
: null,
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold),
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
),
|
),
|
||||||
Text(
|
if (widget.titlesAreLinks)
|
||||||
Uri.parse(urlWithD.key).host,
|
Text(
|
||||||
style: const TextStyle(
|
Uri.parse(entry.key).host,
|
||||||
decoration: TextDecoration.underline, fontSize: 12),
|
style: const TextStyle(
|
||||||
)
|
decoration: TextDecoration.underline, fontSize: 12),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
||||||
var descriptionText = Text(
|
var descriptionText = entry.value.length <= 1
|
||||||
urlWithD.value[1].length > 128
|
? const SizedBox.shrink()
|
||||||
? '${urlWithD.value[1].substring(0, 128)}...'
|
: Text(
|
||||||
: urlWithD.value[1],
|
entry.value[1].length > 128
|
||||||
style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
|
? '${entry.value[1].substring(0, 128)}...'
|
||||||
);
|
: entry.value[1],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontStyle: FontStyle.italic, fontSize: 12),
|
||||||
|
);
|
||||||
|
|
||||||
var selectedUrlsWithDs = urlWithDescriptionSelections.entries
|
var selectedEntries =
|
||||||
.where((e) => e.value)
|
entrySelections.entries.where((e) => e.value).toList();
|
||||||
.toList();
|
|
||||||
|
|
||||||
var singleSelectTile = ListTile(
|
var singleSelectTile = ListTile(
|
||||||
title: urlLink,
|
title: GestureDetector(
|
||||||
subtitle: GestureDetector(
|
onTap: widget.titlesAreLinks
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
selectOnlyOne(urlWithD.key);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: descriptionText,
|
|
||||||
),
|
|
||||||
leading: Radio<String>(
|
|
||||||
value: urlWithD.key,
|
|
||||||
groupValue: selectedUrlsWithDs.isEmpty
|
|
||||||
? null
|
? null
|
||||||
: selectedUrlsWithDs.first.key.key,
|
: () {
|
||||||
|
selectThis(!(entrySelections[entry] ?? false));
|
||||||
|
},
|
||||||
|
child: urlLink,
|
||||||
|
),
|
||||||
|
subtitle: entry.value.length <= 1
|
||||||
|
? null
|
||||||
|
: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
selectOnlyOne(entry.key);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: descriptionText,
|
||||||
|
),
|
||||||
|
leading: Radio<String>(
|
||||||
|
value: entry.key,
|
||||||
|
groupValue: selectedEntries.isEmpty
|
||||||
|
? null
|
||||||
|
: selectedEntries.first.key.key,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectOnlyOne(urlWithD.key);
|
selectOnlyOne(entry.key);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -645,7 +709,7 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
|
|
||||||
var multiSelectTile = Row(children: [
|
var multiSelectTile = Row(children: [
|
||||||
Checkbox(
|
Checkbox(
|
||||||
value: urlWithDescriptionSelections[urlWithD],
|
value: entrySelections[entry],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
selectThis(value);
|
selectThis(value);
|
||||||
}),
|
}),
|
||||||
@@ -660,14 +724,22 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
),
|
),
|
||||||
urlLink,
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: widget.titlesAreLinks
|
||||||
selectThis(
|
? null
|
||||||
!(urlWithDescriptionSelections[urlWithD] ?? false));
|
: () {
|
||||||
},
|
selectThis(!(entrySelections[entry] ?? false));
|
||||||
child: descriptionText,
|
},
|
||||||
|
child: urlLink,
|
||||||
),
|
),
|
||||||
|
entry.value.length <= 1
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
selectThis(!(entrySelections[entry] ?? false));
|
||||||
|
},
|
||||||
|
child: descriptionText,
|
||||||
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
)
|
)
|
||||||
@@ -687,24 +759,18 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
},
|
},
|
||||||
child: Text(tr('cancel'))),
|
child: Text(tr('cancel'))),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed:
|
onPressed: entrySelections.values.where((b) => b).isEmpty
|
||||||
urlWithDescriptionSelections.values.where((b) => b).isEmpty
|
? null
|
||||||
? null
|
: () {
|
||||||
: () {
|
Navigator.of(context).pop(entrySelections.entries
|
||||||
Navigator.of(context).pop(urlWithDescriptionSelections
|
.where((entry) => entry.value)
|
||||||
.entries
|
.map((e) => e.key.key)
|
||||||
.where((entry) => entry.value)
|
.toList());
|
||||||
.map((e) => e.key.key)
|
},
|
||||||
.toList());
|
|
||||||
},
|
|
||||||
child: Text(widget.onlyOneSelectionAllowed
|
child: Text(widget.onlyOneSelectionAllowed
|
||||||
? tr('pick')
|
? tr('pick')
|
||||||
: tr('importX', args: [
|
: tr('selectX', args: [
|
||||||
plural(
|
entrySelections.values.where((b) => b).length.toString()
|
||||||
'url',
|
|
||||||
urlWithDescriptionSelections.values
|
|
||||||
.where((b) => b)
|
|
||||||
.length)
|
|
||||||
])))
|
])))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@@ -6,6 +6,7 @@ import 'dart:convert';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
|
||||||
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
|
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
|
||||||
import 'package:android_intent_plus/flag.dart';
|
import 'package:android_intent_plus/flag.dart';
|
||||||
@@ -139,6 +140,100 @@ List<MapEntry<String, int>> moveStrToEndMapEntryWithCount(
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<File> downloadFileWithRetry(
|
||||||
|
String url, String fileNameNoExt, Function? onProgress, String destDir,
|
||||||
|
{bool useExisting = true,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
int retries = 3}) async {
|
||||||
|
try {
|
||||||
|
return await downloadFile(url, fileNameNoExt, onProgress, destDir,
|
||||||
|
useExisting: useExisting, headers: headers);
|
||||||
|
} catch (e) {
|
||||||
|
if (retries > 0 && e is ClientException) {
|
||||||
|
await Future.delayed(const Duration(seconds: 5));
|
||||||
|
return await downloadFileWithRetry(
|
||||||
|
url, fileNameNoExt, onProgress, destDir,
|
||||||
|
useExisting: useExisting, headers: headers, retries: (retries - 1));
|
||||||
|
} else {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String hashListOfLists(List<List<int>> data) {
|
||||||
|
var bytes = utf8.encode(jsonEncode(data));
|
||||||
|
var digest = sha256.convert(bytes);
|
||||||
|
var hash = digest.toString();
|
||||||
|
return hash.hashCode.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> checkDownloadHash(String url,
|
||||||
|
{int bytesToGrab = 1024, Map<String, String>? headers}) async {
|
||||||
|
var req = Request('GET', Uri.parse(url));
|
||||||
|
if (headers != null) {
|
||||||
|
req.headers.addAll(headers);
|
||||||
|
}
|
||||||
|
req.headers[HttpHeaders.rangeHeader] = 'bytes=0-$bytesToGrab';
|
||||||
|
var client = http.Client();
|
||||||
|
var response = await client.send(req);
|
||||||
|
if (response.statusCode < 200 || response.statusCode > 299) {
|
||||||
|
throw ObtainiumError(response.reasonPhrase ?? tr('unexpectedError'));
|
||||||
|
}
|
||||||
|
List<List<int>> bytes = await response.stream.take(bytesToGrab).toList();
|
||||||
|
return hashListOfLists(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<File> downloadFile(
|
||||||
|
String url, String fileNameNoExt, Function? onProgress, String destDir,
|
||||||
|
{bool useExisting = true, Map<String, String>? headers}) async {
|
||||||
|
var req = Request('GET', Uri.parse(url));
|
||||||
|
if (headers != null) {
|
||||||
|
req.headers.addAll(headers);
|
||||||
|
}
|
||||||
|
var client = http.Client();
|
||||||
|
StreamedResponse response = await client.send(req);
|
||||||
|
String ext =
|
||||||
|
response.headers['content-disposition']?.split('.').last ?? 'apk';
|
||||||
|
if (ext.endsWith('"') || ext.endsWith("other")) {
|
||||||
|
ext = ext.substring(0, ext.length - 1);
|
||||||
|
}
|
||||||
|
if (url.toLowerCase().endsWith('.apk') && ext != 'apk') {
|
||||||
|
ext = 'apk';
|
||||||
|
}
|
||||||
|
File downloadedFile = File('$destDir/$fileNameNoExt.$ext');
|
||||||
|
if (!(downloadedFile.existsSync() && useExisting)) {
|
||||||
|
File tempDownloadedFile = File('${downloadedFile.path}.part');
|
||||||
|
if (tempDownloadedFile.existsSync()) {
|
||||||
|
tempDownloadedFile.deleteSync(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);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}).pipe(sink);
|
||||||
|
await sink.close();
|
||||||
|
progress = null;
|
||||||
|
if (onProgress != null) {
|
||||||
|
onProgress(progress);
|
||||||
|
}
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
tempDownloadedFile.deleteSync(recursive: true);
|
||||||
|
throw response.reasonPhrase ?? tr('unexpectedError');
|
||||||
|
}
|
||||||
|
tempDownloadedFile.renameSync(downloadedFile.path);
|
||||||
|
} else {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
return downloadedFile;
|
||||||
|
}
|
||||||
|
|
||||||
class AppsProvider with ChangeNotifier {
|
class AppsProvider with ChangeNotifier {
|
||||||
// In memory App state (should always be kept in sync with local storage versions)
|
// In memory App state (should always be kept in sync with local storage versions)
|
||||||
Map<String, AppInMemory> apps = {};
|
Map<String, AppInMemory> apps = {};
|
||||||
@@ -192,77 +287,6 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<File> downloadFileWithRetry(
|
|
||||||
String url, String fileNameNoExt, Function? onProgress,
|
|
||||||
{bool useExisting = true,
|
|
||||||
Map<String, String>? headers,
|
|
||||||
int retries = 3}) async {
|
|
||||||
try {
|
|
||||||
return await downloadFile(url, fileNameNoExt, onProgress,
|
|
||||||
useExisting: useExisting, headers: headers);
|
|
||||||
} catch (e) {
|
|
||||||
if (retries > 0 && e is ClientException) {
|
|
||||||
await Future.delayed(const Duration(seconds: 5));
|
|
||||||
return await downloadFileWithRetry(url, fileNameNoExt, onProgress,
|
|
||||||
useExisting: useExisting, headers: headers, retries: (retries - 1));
|
|
||||||
} else {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<File> downloadFile(
|
|
||||||
String url, String fileNameNoExt, Function? onProgress,
|
|
||||||
{bool useExisting = true, Map<String, String>? headers}) async {
|
|
||||||
var destDir = APKDir.path;
|
|
||||||
var req = Request('GET', Uri.parse(url));
|
|
||||||
if (headers != null) {
|
|
||||||
req.headers.addAll(headers);
|
|
||||||
}
|
|
||||||
var client = http.Client();
|
|
||||||
StreamedResponse response = await client.send(req);
|
|
||||||
String ext =
|
|
||||||
response.headers['content-disposition']?.split('.').last ?? 'apk';
|
|
||||||
if (ext.endsWith('"') || ext.endsWith("other")) {
|
|
||||||
ext = ext.substring(0, ext.length - 1);
|
|
||||||
}
|
|
||||||
if (url.toLowerCase().endsWith('.apk') && ext != 'apk') {
|
|
||||||
ext = 'apk';
|
|
||||||
}
|
|
||||||
File downloadedFile = File('$destDir/$fileNameNoExt.$ext');
|
|
||||||
if (!(downloadedFile.existsSync() && useExisting)) {
|
|
||||||
File tempDownloadedFile = File('${downloadedFile.path}.part');
|
|
||||||
if (tempDownloadedFile.existsSync()) {
|
|
||||||
tempDownloadedFile.deleteSync(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);
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}).pipe(sink);
|
|
||||||
await sink.close();
|
|
||||||
progress = null;
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(progress);
|
|
||||||
}
|
|
||||||
if (response.statusCode != 200) {
|
|
||||||
tempDownloadedFile.deleteSync(recursive: true);
|
|
||||||
throw response.reasonPhrase ?? tr('unexpectedError');
|
|
||||||
}
|
|
||||||
tempDownloadedFile.renameSync(downloadedFile.path);
|
|
||||||
} else {
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
return downloadedFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<File> handleAPKIDChange(App app, PackageInfo? newInfo,
|
Future<File> handleAPKIDChange(App app, PackageInfo? newInfo,
|
||||||
File downloadedFile, String downloadUrl) async {
|
File downloadedFile, String downloadUrl) async {
|
||||||
// If the APK package ID is different from the App ID, it is either new (using a placeholder ID) or the ID has changed
|
// If the APK package ID is different from the App ID, it is either new (using a placeholder ID) or the ID has changed
|
||||||
@@ -322,7 +346,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
notificationsProvider?.notify(notif);
|
notificationsProvider?.notify(notif);
|
||||||
}
|
}
|
||||||
prevProg = prog;
|
prevProg = prog;
|
||||||
});
|
}, APKDir.path);
|
||||||
// Set to 90 for remaining steps, will make null in 'finally'
|
// Set to 90 for remaining steps, will make null in 'finally'
|
||||||
if (apps[app.id] != null) {
|
if (apps[app.id] != null) {
|
||||||
apps[app.id]!.downloadProgress = -1;
|
apps[app.id]!.downloadProgress = -1;
|
||||||
|
@@ -67,10 +67,11 @@ appJSONCompatibilityModifiers(Map<String, dynamic> json) {
|
|||||||
.reduce((value, element) => [...value, ...element]);
|
.reduce((value, element) => [...value, ...element]);
|
||||||
Map<String, dynamic> additionalSettings =
|
Map<String, dynamic> additionalSettings =
|
||||||
getDefaultValuesFromFormItems([formItems]);
|
getDefaultValuesFromFormItems([formItems]);
|
||||||
|
Map<String, dynamic> originalAdditionalSettings = {};
|
||||||
if (json['additionalSettings'] != null) {
|
if (json['additionalSettings'] != null) {
|
||||||
additionalSettings.addEntries(
|
originalAdditionalSettings =
|
||||||
Map<String, dynamic>.from(jsonDecode(json['additionalSettings']))
|
Map<String, dynamic>.from(jsonDecode(json['additionalSettings']));
|
||||||
.entries);
|
additionalSettings.addEntries(originalAdditionalSettings.entries);
|
||||||
}
|
}
|
||||||
// If needed, migrate old-style additionalData to newer-style additionalSettings (V1)
|
// If needed, migrate old-style additionalData to newer-style additionalSettings (V1)
|
||||||
if (json['additionalData'] != null) {
|
if (json['additionalData'] != null) {
|
||||||
@@ -134,6 +135,11 @@ appJSONCompatibilityModifiers(Map<String, dynamic> json) {
|
|||||||
if (additionalSettings['autoApkFilterByArch'] == null) {
|
if (additionalSettings['autoApkFilterByArch'] == null) {
|
||||||
additionalSettings['autoApkFilterByArch'] = false;
|
additionalSettings['autoApkFilterByArch'] = false;
|
||||||
}
|
}
|
||||||
|
// HTML 'fixed URL' support should be disabled if it previously did not exist
|
||||||
|
if (source.runtimeType == HTML().runtimeType &&
|
||||||
|
originalAdditionalSettings['supportFixedAPKURL'] == null) {
|
||||||
|
additionalSettings['supportFixedAPKURL'] = false;
|
||||||
|
}
|
||||||
json['additionalSettings'] = jsonEncode(additionalSettings);
|
json['additionalSettings'] = jsonEncode(additionalSettings);
|
||||||
// F-Droid no longer needs cloudflare exception since override can be used - migrate apps appropriately
|
// F-Droid no longer needs cloudflare exception since override can be used - migrate apps appropriately
|
||||||
// This allows us to reverse the changes made for issue #418 (support cloudflare.f-droid)
|
// This allows us to reverse the changes made for issue #418 (support cloudflare.f-droid)
|
||||||
@@ -596,7 +602,7 @@ class SourceProvider {
|
|||||||
AppSource? source;
|
AppSource? source;
|
||||||
for (var s in sources.where((element) => element.host != null)) {
|
for (var s in sources.where((element) => element.host != null)) {
|
||||||
if (RegExp(
|
if (RegExp(
|
||||||
'://(${s.allowSubDomains ? '([^\\.]+\\.)*' : ''}|www\\.)${s.host}(/|\\z)?')
|
'://${s.allowSubDomains ? '([^\\.]+\\.)*' : '(www\\.)?'}${s.host}(/|\\z)?')
|
||||||
.hasMatch(url)) {
|
.hasMatch(url)) {
|
||||||
source = s;
|
source = s;
|
||||||
break;
|
break;
|
||||||
|
54
pubspec.lock
54
pubspec.lock
@@ -118,10 +118,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: connectivity_plus
|
name: connectivity_plus
|
||||||
sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f
|
sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.1"
|
version: "5.0.2"
|
||||||
connectivity_plus_platform_interface:
|
connectivity_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -142,12 +142,12 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c"
|
sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3+6"
|
version: "0.3.3+7"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||||
@@ -182,10 +182,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419"
|
sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.1.0"
|
version: "9.1.1"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -291,10 +291,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_local_notifications
|
name: flutter_local_notifications
|
||||||
sha256: "6d11ea777496061e583623aaf31923f93a9409ef8fcaeeefdd6cd78bf4fe5bb3"
|
sha256: bb5cd63ff7c91d6efe452e41d0d0ae6348925c82eafd10ce170ef585ea04776e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "16.1.0"
|
version: "16.2.0"
|
||||||
flutter_local_notifications_linux:
|
flutter_local_notifications_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -538,42 +538,50 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: permission_handler
|
name: permission_handler
|
||||||
sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8"
|
sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.1"
|
version: "11.1.0"
|
||||||
permission_handler_android:
|
permission_handler_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_android
|
name: permission_handler_android
|
||||||
sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e
|
sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.1.0"
|
version: "12.0.1"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_apple
|
name: permission_handler_apple
|
||||||
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
|
sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.1.4"
|
version: "9.2.0"
|
||||||
|
permission_handler_html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_html
|
||||||
|
sha256: d96ff56a757b7f04fa825c469d296c5aebc55f743e87bd639fef91a466a24da8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.0+1"
|
||||||
permission_handler_platform_interface:
|
permission_handler_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_platform_interface
|
name: permission_handler_platform_interface
|
||||||
sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
|
sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.12.0"
|
version: "4.0.2"
|
||||||
permission_handler_windows:
|
permission_handler_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_windows
|
name: permission_handler_windows
|
||||||
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
|
sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.3"
|
version: "0.2.0"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -847,10 +855,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2"
|
sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.1"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -903,10 +911,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_platform_interface
|
name: webview_flutter_platform_interface
|
||||||
sha256: "6d9213c65f1060116757a7c473247c60f3f7f332cac33dc417c9e362a9a13e4f"
|
sha256: adb8c03c2be231bea5a8ed0e9039e9d18dbb049603376beaefa15393ede468a5
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.0"
|
version: "2.7.0"
|
||||||
webview_flutter_wkwebview:
|
webview_flutter_wkwebview:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.14.34+228 # When changing this, update the tag in main() accordingly
|
version: 0.14.35+229 # When changing this, update the tag in main() accordingly
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
@@ -66,6 +66,7 @@ dependencies:
|
|||||||
hsluv: ^1.1.3
|
hsluv: ^1.1.3
|
||||||
connectivity_plus: ^5.0.0
|
connectivity_plus: ^5.0.0
|
||||||
shared_storage: ^0.8.0
|
shared_storage: ^0.8.0
|
||||||
|
crypto: ^3.0.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user