Compare commits
19 Commits
v0.10.5-be
...
v0.10.7-be
Author | SHA1 | Date | |
---|---|---|---|
|
bbaa42fb01 | ||
|
4fe311bc03 | ||
|
ea68b97ff7 | ||
|
6e0f6b528e | ||
|
a2c227931e | ||
|
15ad3bb439 | ||
|
b03d7fba1a | ||
|
31c491d7c5 | ||
|
71c80f11f5 | ||
|
eef4d33431 | ||
|
d56342e907 | ||
|
c72c0fdb57 | ||
|
ffe29009ed | ||
|
60e3b68ebd | ||
|
ee4d0f259f | ||
|
0ecfbef0a0 | ||
|
1b60e75ca7 | ||
|
abcfa389e8 | ||
|
a64bd67ef1 |
@@ -31,4 +31,4 @@ Currently supported App sources:
|
|||||||
|
|
||||||
| <img src="./assets/screenshots/1.apps.png" alt="Apps Page" /> | <img src="./assets/screenshots/2.dark_theme.png" alt="Dark Theme" /> | <img src="./assets/screenshots/3.material_you.png" alt="Material You" /> |
|
| <img src="./assets/screenshots/1.apps.png" alt="Apps Page" /> | <img src="./assets/screenshots/2.dark_theme.png" alt="Dark Theme" /> | <img src="./assets/screenshots/3.material_you.png" alt="Material You" /> |
|
||||||
| ------------------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
| ------------------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
||||||
| <img src="./assets/screenshots/4.app.png" alt="App Page" /> | <img src="./assets/screenshots/5.apk_picker.png" alt="Multiple APK Support" /> | <img src="./assets/screenshots/6.apk_install.png" alt="App Installation" /> |
|
| <img src="./assets/screenshots/4.app.png" alt="App Page" /> | <img src="./assets/screenshots/5.app_opts.png" alt="App Options" /> | <img src="./assets/screenshots/6.app_webview.png" alt="App Web View" /> |
|
||||||
|
@@ -3,7 +3,8 @@
|
|||||||
<application
|
<application
|
||||||
android:label="Obtainium"
|
android:label="Obtainium"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:requestLegacyExternalStorage="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@@ -51,7 +52,8 @@
|
|||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="28"/>
|
android:maxSdkVersion="29"/>
|
||||||
</manifest>
|
</manifest>
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 234 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 238 KiB |
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 188 KiB |
BIN
assets/screenshots/5.app_opts.png
Normal file
After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 192 KiB |
BIN
assets/screenshots/6.app_webview.png
Normal file
After Width: | Height: | Size: 262 KiB |
@@ -74,7 +74,6 @@
|
|||||||
"changeX": "Ändern {}",
|
"changeX": "Ändern {}",
|
||||||
"installUpdateApps": "Apps installieren/aktualisieren",
|
"installUpdateApps": "Apps installieren/aktualisieren",
|
||||||
"installUpdateSelectedApps": "Ausgewählte Apps installieren/aktualisieren",
|
"installUpdateSelectedApps": "Ausgewählte Apps installieren/aktualisieren",
|
||||||
"onlyWorksWithNonEVDApps": "Funktioniert nur bei Apps, deren Installationsstatus nicht automatisch erkannt werden kann (ungewöhnlich).",
|
|
||||||
"markXSelectedAppsAsUpdated": "Markiere {} ausgewählte Apps als aktuell?",
|
"markXSelectedAppsAsUpdated": "Markiere {} ausgewählte Apps als aktuell?",
|
||||||
"no": "Nein",
|
"no": "Nein",
|
||||||
"yes": "Ja",
|
"yes": "Ja",
|
||||||
@@ -178,7 +177,6 @@
|
|||||||
"installedVersionX": "Installierte Version: {}",
|
"installedVersionX": "Installierte Version: {}",
|
||||||
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
|
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
|
||||||
"remove": "Entfernen",
|
"remove": "Entfernen",
|
||||||
"removeAppQuestion": "App entfernen?",
|
|
||||||
"yesMarkUpdated": "Ja, als aktualisiert markieren",
|
"yesMarkUpdated": "Ja, als aktualisiert markieren",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid",
|
||||||
"appIdOrName": "App ID oder Name",
|
"appIdOrName": "App ID oder Name",
|
||||||
@@ -212,6 +210,13 @@
|
|||||||
"storagePermissionDenied": "Storage permission denied",
|
"storagePermissionDenied": "Storage permission denied",
|
||||||
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
|
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
|
||||||
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
||||||
|
"removeFromObtainium": "Remove from Obtainium",
|
||||||
|
"uninstallFromDevice": "Uninstall from Device",
|
||||||
|
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
|
||||||
|
"removeAppQuestion": {
|
||||||
|
"one": "App entfernen?",
|
||||||
|
"other": "App entfernen?"
|
||||||
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut",
|
"one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut",
|
||||||
"other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut"
|
"other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut"
|
||||||
|
@@ -74,7 +74,6 @@
|
|||||||
"changeX": "Change {}",
|
"changeX": "Change {}",
|
||||||
"installUpdateApps": "Install/Update Apps",
|
"installUpdateApps": "Install/Update Apps",
|
||||||
"installUpdateSelectedApps": "Install/Update Selected Apps",
|
"installUpdateSelectedApps": "Install/Update Selected Apps",
|
||||||
"onlyWorksWithNonEVDApps": "Only works for Apps whose install status cannot be automatically detected (uncommon).",
|
|
||||||
"markXSelectedAppsAsUpdated": "Mark {} Selected Apps as Updated?",
|
"markXSelectedAppsAsUpdated": "Mark {} Selected Apps as Updated?",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
@@ -178,7 +177,6 @@
|
|||||||
"installedVersionX": "Installed Version: {}",
|
"installedVersionX": "Installed Version: {}",
|
||||||
"lastUpdateCheckX": "Last Update Check: {}",
|
"lastUpdateCheckX": "Last Update Check: {}",
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
"removeAppQuestion": "Remove App?",
|
|
||||||
"yesMarkUpdated": "Yes, Mark as Updated",
|
"yesMarkUpdated": "Yes, Mark as Updated",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid",
|
||||||
"appIdOrName": "App ID or Name",
|
"appIdOrName": "App ID or Name",
|
||||||
@@ -212,6 +210,13 @@
|
|||||||
"storagePermissionDenied": "Storage permission denied",
|
"storagePermissionDenied": "Storage permission denied",
|
||||||
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
|
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
|
||||||
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
||||||
|
"removeFromObtainium": "Remove from Obtainium",
|
||||||
|
"uninstallFromDevice": "Uninstall from Device",
|
||||||
|
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
|
||||||
|
"removeAppQuestion": {
|
||||||
|
"one": "Remove App?",
|
||||||
|
"other": "Remove Apps?"
|
||||||
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Too many requests (rate limited) - try again in {} minute",
|
"one": "Too many requests (rate limited) - try again in {} minute",
|
||||||
"other": "Too many requests (rate limited) - try again in {} minutes"
|
"other": "Too many requests (rate limited) - try again in {} minutes"
|
||||||
|
@@ -74,7 +74,6 @@
|
|||||||
"changeX": "Változás {}",
|
"changeX": "Változás {}",
|
||||||
"installUpdateApps": "Appok telepítése/frissítése",
|
"installUpdateApps": "Appok telepítése/frissítése",
|
||||||
"installUpdateSelectedApps": "Telepítse/frissítse a kiválasztott appokat",
|
"installUpdateSelectedApps": "Telepítse/frissítse a kiválasztott appokat",
|
||||||
"onlyWorksWithNonEVDApps": "Csak azoknál az alkalmazásoknál működik, amelyek telepítési állapota nem észlelhető autom. (nem gyakori).",
|
|
||||||
"markXSelectedAppsAsUpdated": "Megjelöl {} kiválasztott alkalmazást frissítettként?",
|
"markXSelectedAppsAsUpdated": "Megjelöl {} kiválasztott alkalmazást frissítettként?",
|
||||||
"no": "Nem",
|
"no": "Nem",
|
||||||
"yes": "Igen",
|
"yes": "Igen",
|
||||||
@@ -178,7 +177,6 @@
|
|||||||
"installedVersionX": "Telepített verzió: {}",
|
"installedVersionX": "Telepített verzió: {}",
|
||||||
"lastUpdateCheckX": "Frissítés ellenőrizve: {}",
|
"lastUpdateCheckX": "Frissítés ellenőrizve: {}",
|
||||||
"remove": "Eltávolítás",
|
"remove": "Eltávolítás",
|
||||||
"removeAppQuestion": "Eltávolítja az alkalmazást?",
|
|
||||||
"yesMarkUpdated": "Igen, megjelölés frissítettként",
|
"yesMarkUpdated": "Igen, megjelölés frissítettként",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid",
|
||||||
"appIdOrName": "App ID vagy név",
|
"appIdOrName": "App ID vagy név",
|
||||||
@@ -211,6 +209,13 @@
|
|||||||
"storagePermissionDenied": "Tárhely engedély megtagadva",
|
"storagePermissionDenied": "Tárhely engedély megtagadva",
|
||||||
"selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.",
|
"selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.",
|
||||||
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
||||||
|
"removeFromObtainium": "Remove from Obtainium",
|
||||||
|
"uninstallFromDevice": "Uninstall from Device",
|
||||||
|
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
|
||||||
|
"removeAppQuestion": {
|
||||||
|
"one": "Eltávolítja az alkalmazást?",
|
||||||
|
"other": "Eltávolítja az alkalmazást?"
|
||||||
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva",
|
"one": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva",
|
||||||
"other": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva"
|
"other": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva"
|
||||||
|
@@ -74,7 +74,6 @@
|
|||||||
"changeX": "Modifica {}",
|
"changeX": "Modifica {}",
|
||||||
"installUpdateApps": "Installa/Aggiorna App",
|
"installUpdateApps": "Installa/Aggiorna App",
|
||||||
"installUpdateSelectedApps": "Installa/Aggiorna le App selezionate",
|
"installUpdateSelectedApps": "Installa/Aggiorna le App selezionate",
|
||||||
"onlyWorksWithNonEVDApps": "Funziona solo per le App il cui stato d'installazione non può essere rilevato automaticamente (inconsueto).",
|
|
||||||
"markXSelectedAppsAsUpdated": "Contrassegnare le {} App selezionate come aggiornate?",
|
"markXSelectedAppsAsUpdated": "Contrassegnare le {} App selezionate come aggiornate?",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"yes": "Sì",
|
"yes": "Sì",
|
||||||
@@ -178,7 +177,6 @@
|
|||||||
"installedVersionX": "Versione installata: {}",
|
"installedVersionX": "Versione installata: {}",
|
||||||
"lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}",
|
"lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}",
|
||||||
"remove": "Rimuovi",
|
"remove": "Rimuovi",
|
||||||
"removeAppQuestion": "Rimuovere l'App?",
|
|
||||||
"yesMarkUpdated": "Sì, contrassegna come aggiornato",
|
"yesMarkUpdated": "Sì, contrassegna come aggiornato",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid",
|
||||||
"appIdOrName": "ID o nome dell'App",
|
"appIdOrName": "ID o nome dell'App",
|
||||||
@@ -212,6 +210,13 @@
|
|||||||
"storagePermissionDenied": "Storage permission denied",
|
"storagePermissionDenied": "Storage permission denied",
|
||||||
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
|
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
|
||||||
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
||||||
|
"removeFromObtainium": "Remove from Obtainium",
|
||||||
|
"uninstallFromDevice": "Uninstall from Device",
|
||||||
|
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
|
||||||
|
"removeAppQuestion": {
|
||||||
|
"one": "Rimuovere l'App?",
|
||||||
|
"other": "Rimuovere l'App?"
|
||||||
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Troppe richieste (traffico limitato) - riprova tra {} minuto",
|
"one": "Troppe richieste (traffico limitato) - riprova tra {} minuto",
|
||||||
"other": "Troppe richieste (traffico limitato) - riprova tra {} minuti"
|
"other": "Troppe richieste (traffico limitato) - riprova tra {} minuti"
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
"appIdMismatch": "ダウンロードしたパッケージのIDが既存のApp IDと一致しません",
|
"appIdMismatch": "ダウンロードしたパッケージのIDが既存のApp IDと一致しません",
|
||||||
"functionNotImplemented": "このクラスはこの機能を実装していません",
|
"functionNotImplemented": "このクラスはこの機能を実装していません",
|
||||||
"placeholder": "プレースホルダー",
|
"placeholder": "プレースホルダー",
|
||||||
"someErrors": "いくつかのエラーが発生しました",
|
"someErrors": "何らかのエラーが発生しました",
|
||||||
"unexpectedError": "予期せぬエラーが発生しました",
|
"unexpectedError": "予期せぬエラーが発生しました",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"and": "と",
|
"and": "と",
|
||||||
@@ -74,7 +74,6 @@
|
|||||||
"changeX": "{} を変更する",
|
"changeX": "{} を変更する",
|
||||||
"installUpdateApps": "アプリのインストール/アップデート",
|
"installUpdateApps": "アプリのインストール/アップデート",
|
||||||
"installUpdateSelectedApps": "選択したアプリのインストール/アップデート",
|
"installUpdateSelectedApps": "選択したアプリのインストール/アップデート",
|
||||||
"onlyWorksWithNonEVDApps": "インストール状況を自動検出できないアプリ(一般的でないもの)のみ動作します。",
|
|
||||||
"markXSelectedAppsAsUpdated": "{}個の選択したアプリをアップデート済みとしてマークしますか?",
|
"markXSelectedAppsAsUpdated": "{}個の選択したアプリをアップデート済みとしてマークしますか?",
|
||||||
"no": "いいえ",
|
"no": "いいえ",
|
||||||
"yes": "はい",
|
"yes": "はい",
|
||||||
@@ -82,7 +81,7 @@
|
|||||||
"pinToTop": "トップに固定",
|
"pinToTop": "トップに固定",
|
||||||
"unpinFromTop": "トップから固定解除",
|
"unpinFromTop": "トップから固定解除",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "選択したアプリのインストール状態をリセットしますか?",
|
"resetInstallStatusForSelectedAppsQuestion": "選択したアプリのインストール状態をリセットしますか?",
|
||||||
"installStatusOfXWillBeResetExplanation": "選択したアプリのインストール状態がリセットされます。\n\nアップデートに失敗するなどして、Obtainiumに表示されるアプリのバージョンが正しくない場合に役立ちます。",
|
"installStatusOfXWillBeResetExplanation": "選択したアプリのインストール状態がリセットされます。\n\nアップデートに失敗した場合など、Obtainiumに表示されるアプリのバージョンが正しくない場合に有効です。",
|
||||||
"shareSelectedAppURLs": "選択したアプリのURLを共有する",
|
"shareSelectedAppURLs": "選択したアプリのURLを共有する",
|
||||||
"resetInstallStatus": "インストール状態をリセットする",
|
"resetInstallStatus": "インストール状態をリセットする",
|
||||||
"more": "もっと見る",
|
"more": "もっと見る",
|
||||||
@@ -109,7 +108,7 @@
|
|||||||
"searchX": "{}で検索",
|
"searchX": "{}で検索",
|
||||||
"noResults": "結果は見つかりませんでした",
|
"noResults": "結果は見つかりませんでした",
|
||||||
"importX": "{}をインポートする",
|
"importX": "{}をインポートする",
|
||||||
"importedAppsIdDisclaimer": "インポートしたアプリが「未インストール」と表示されることがあります。\nこれを解決するには、Obtainiumから再インストールしてください。\nアプリのデータには影響しません。\n\nURLとサードパーティーのインポートメソッドにのみ影響します。",
|
"importedAppsIdDisclaimer": "インポートしたアプリが「未インストール」と表示されることがあります。\nこれを解決するには、Obtainiumから再インストールしてください。\nアプリのデータには影響しません。\n\nURLとサードパーティのインポートメソッドにのみ影響します。",
|
||||||
"importErrors": "インポートエラー",
|
"importErrors": "インポートエラー",
|
||||||
"importedXOfYApps": "{} / {} アプリをインポートしました",
|
"importedXOfYApps": "{} / {} アプリをインポートしました",
|
||||||
"followingURLsHadErrors": "以下のURLでエラーが発生しました:",
|
"followingURLsHadErrors": "以下のURLでエラーが発生しました:",
|
||||||
@@ -133,7 +132,7 @@
|
|||||||
"bgUpdateCheckInterval": "バックグラウンドでのアップデート確認の間隔",
|
"bgUpdateCheckInterval": "バックグラウンドでのアップデート確認の間隔",
|
||||||
"neverManualOnly": "手動",
|
"neverManualOnly": "手動",
|
||||||
"appearance": "外観",
|
"appearance": "外観",
|
||||||
"showWebInAppView": "アプリビューにソースウェブページを表示する",
|
"showWebInAppView": "アプリページにソースのWebページを表示する",
|
||||||
"pinUpdates": "アップデートがあるアプリをトップに固定する",
|
"pinUpdates": "アップデートがあるアプリをトップに固定する",
|
||||||
"updates": "アップデート",
|
"updates": "アップデート",
|
||||||
"sourceSpecific": "Github アクセストークン",
|
"sourceSpecific": "Github アクセストークン",
|
||||||
@@ -178,13 +177,12 @@
|
|||||||
"installedVersionX": "インストールされたバージョン: {}",
|
"installedVersionX": "インストールされたバージョン: {}",
|
||||||
"lastUpdateCheckX": "最終アップデート確認: {}",
|
"lastUpdateCheckX": "最終アップデート確認: {}",
|
||||||
"remove": "削除",
|
"remove": "削除",
|
||||||
"removeAppQuestion": "アプリを削除しますか?",
|
|
||||||
"yesMarkUpdated": "はい、アップデート済みとしてマークします",
|
"yesMarkUpdated": "はい、アップデート済みとしてマークします",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid",
|
||||||
"appIdOrName": "アプリのIDまたは名前",
|
"appIdOrName": "アプリのIDまたは名前",
|
||||||
"appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした",
|
"appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした",
|
||||||
"reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります",
|
"reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
"fdroidThirdPartyRepo": "F-Droid サードパーティリポジトリ",
|
||||||
"steam": "Steam",
|
"steam": "Steam",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamMobile": "Steam Mobile",
|
||||||
"steamChat": "Steam Chat",
|
"steamChat": "Steam Chat",
|
||||||
@@ -211,7 +209,14 @@
|
|||||||
"language": "言語",
|
"language": "言語",
|
||||||
"storagePermissionDenied": "ストレージ権限が拒否されました",
|
"storagePermissionDenied": "ストレージ権限が拒否されました",
|
||||||
"selectedCategorizeWarning": "これにより、選択したアプリの既存のカテゴリ設定がすべて置き換えられます。",
|
"selectedCategorizeWarning": "これにより、選択したアプリの既存のカテゴリ設定がすべて置き換えられます。",
|
||||||
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
"filterAPKsByRegEx": "正規表現でAPKを絞り込む",
|
||||||
|
"removeFromObtainium": "Remove from Obtainium",
|
||||||
|
"uninstallFromDevice": "Uninstall from Device",
|
||||||
|
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
|
||||||
|
"removeAppQuestion": {
|
||||||
|
"one": "アプリを削除しますか?",
|
||||||
|
"other": "アプリを削除しますか?"
|
||||||
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください",
|
"one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください",
|
||||||
"other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください"
|
"other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください"
|
||||||
|
@@ -178,7 +178,6 @@
|
|||||||
"installedVersionX": "已安装: {}",
|
"installedVersionX": "已安装: {}",
|
||||||
"lastUpdateCheckX": "最后检查: {}",
|
"lastUpdateCheckX": "最后检查: {}",
|
||||||
"remove": "删除",
|
"remove": "删除",
|
||||||
"removeAppQuestion": "删除应用?",
|
|
||||||
"yesMarkUpdated": "'是的,标为已更新",
|
"yesMarkUpdated": "'是的,标为已更新",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid",
|
||||||
"appIdOrName": "应用 ID 或名称",
|
"appIdOrName": "应用 ID 或名称",
|
||||||
@@ -212,6 +211,12 @@
|
|||||||
"storagePermissionDenied": "存储权限已被拒绝",
|
"storagePermissionDenied": "存储权限已被拒绝",
|
||||||
"selectedCategorizeWarning": "这将取代所选应用程序的任何现有类别",
|
"selectedCategorizeWarning": "这将取代所选应用程序的任何现有类别",
|
||||||
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
||||||
|
"removeFromObtainium": "Remove from Obtainium",
|
||||||
|
"uninstallFromDevice": "Uninstall from Device",
|
||||||
|
"removeAppQuestion": {
|
||||||
|
"one": "删除应用?",
|
||||||
|
"other": "删除应用?"
|
||||||
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "请求过多 (API 限制) - 在 {} 分钟后重试",
|
"one": "请求过多 (API 限制) - 在 {} 分钟后重试",
|
||||||
"other": "请求过多 (API 限制) - 在 {} 分钟后重试"
|
"other": "请求过多 (API 限制) - 在 {} 分钟后重试"
|
||||||
|
@@ -150,6 +150,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
Map<String, dynamic> values = {};
|
Map<String, dynamic> values = {};
|
||||||
late List<List<Widget>> formInputs;
|
late List<List<Widget>> formInputs;
|
||||||
List<List<Widget>> rows = [];
|
List<List<Widget>> rows = [];
|
||||||
|
String? initKey;
|
||||||
|
|
||||||
// If any value changes, call this to update the parent with value and validity
|
// If any value changes, call this to update the parent with value and validity
|
||||||
void someValueChanged({bool isBuilding = false}) {
|
void someValueChanged({bool isBuilding = false}) {
|
||||||
@@ -169,13 +170,10 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
widget.onValueChanges(returnValues, valid, isBuilding);
|
widget.onValueChanges(returnValues, valid, isBuilding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
initForm() {
|
||||||
void initState() {
|
initKey = widget.key.toString();
|
||||||
super.initState();
|
|
||||||
|
|
||||||
// Initialize form values as all empty
|
// Initialize form values as all empty
|
||||||
values.clear();
|
values.clear();
|
||||||
int j = 0;
|
|
||||||
for (var row in widget.items) {
|
for (var row in widget.items) {
|
||||||
for (var e in row) {
|
for (var e in row) {
|
||||||
values[e.key] = e.defaultValue;
|
values[e.key] = e.defaultValue;
|
||||||
@@ -245,8 +243,17 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
someValueChanged(isBuilding: true);
|
someValueChanged(isBuilding: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
initForm();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (widget.key.toString() != initKey) {
|
||||||
|
initForm();
|
||||||
|
}
|
||||||
for (var r = 0; r < formInputs.length; r++) {
|
for (var r = 0; r < formInputs.length; r++) {
|
||||||
for (var e = 0; e < formInputs[r].length; e++) {
|
for (var e = 0; e < formInputs[r].length; e++) {
|
||||||
if (widget.items[r][e] is GeneratedFormSwitch) {
|
if (widget.items[r][e] is GeneratedFormSwitch) {
|
||||||
|
@@ -21,7 +21,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.10.5';
|
const String currentVersion = '0.10.7';
|
||||||
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
|
||||||
|
|
||||||
|
@@ -32,6 +32,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
Map<String, dynamic> additionalSettings = {};
|
Map<String, dynamic> additionalSettings = {};
|
||||||
bool additionalSettingsValid = true;
|
bool additionalSettingsValid = true;
|
||||||
List<String> pickedCategories = [];
|
List<String> pickedCategories = [];
|
||||||
|
int searchnum = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -40,10 +41,14 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
|
|
||||||
bool doingSomething = gettingAppInfo || searching;
|
bool doingSomething = gettingAppInfo || searching;
|
||||||
|
|
||||||
changeUserInput(String input, bool valid, bool isBuilding) {
|
changeUserInput(String input, bool valid, bool isBuilding,
|
||||||
|
{bool isSearch = false}) {
|
||||||
userInput = input;
|
userInput = input;
|
||||||
if (!isBuilding) {
|
if (!isBuilding) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
if (isSearch) {
|
||||||
|
searchnum++;
|
||||||
|
}
|
||||||
var source = valid ? sourceProvider.getSource(userInput) : null;
|
var source = valid ? sourceProvider.getSource(userInput) : null;
|
||||||
if (pickedSource.runtimeType != source.runtimeType) {
|
if (pickedSource.runtimeType != source.runtimeType) {
|
||||||
pickedSource = source;
|
pickedSource = source;
|
||||||
@@ -169,10 +174,12 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GeneratedForm(
|
child: GeneratedForm(
|
||||||
|
key: Key(searchnum.toString()),
|
||||||
items: [
|
items: [
|
||||||
[
|
[
|
||||||
GeneratedFormTextField('appSourceURL',
|
GeneratedFormTextField('appSourceURL',
|
||||||
label: tr('appSourceURL'),
|
label: tr('appSourceURL'),
|
||||||
|
defaultValue: userInput,
|
||||||
additionalValidators: [
|
additionalValidators: [
|
||||||
(value) {
|
(value) {
|
||||||
try {
|
try {
|
||||||
@@ -296,8 +303,8 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
if (selectedUrls != null &&
|
if (selectedUrls != null &&
|
||||||
selectedUrls.isNotEmpty) {
|
selectedUrls.isNotEmpty) {
|
||||||
changeUserInput(
|
changeUserInput(
|
||||||
selectedUrls[0], true, false);
|
selectedUrls[0], true, false,
|
||||||
addApp(resetUserInputAfter: true);
|
isSearch: true);
|
||||||
}
|
}
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
showError(e, context);
|
showError(e, context);
|
||||||
@@ -327,6 +334,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
height: 16,
|
height: 16,
|
||||||
),
|
),
|
||||||
GeneratedForm(
|
GeneratedForm(
|
||||||
|
key: Key(pickedSource.runtimeType.toString()),
|
||||||
items: pickedSource!
|
items: pickedSource!
|
||||||
.combinedAppSpecificSettingFormItems,
|
.combinedAppSpecificSettingFormItems,
|
||||||
onValueChanges: (values, valid, isBuilding) {
|
onValueChanges: (values, valid, isBuilding) {
|
||||||
|
@@ -42,6 +42,8 @@ class _AppPageState extends State<AppPage> {
|
|||||||
getUpdate(app.app.id);
|
getUpdate(app.app.id);
|
||||||
}
|
}
|
||||||
var trackOnly = app?.app.additionalSettings['trackOnly'] == true;
|
var trackOnly = app?.app.additionalSettings['trackOnly'] == true;
|
||||||
|
var noVersionDetection =
|
||||||
|
app?.app.additionalSettings['noVersionDetection'] == true;
|
||||||
|
|
||||||
var infoColumn = Column(
|
var infoColumn = Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@@ -190,8 +192,9 @@ class _AppPageState extends State<AppPage> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
if (app?.app.installedVersion != null &&
|
if (noVersionDetection &&
|
||||||
!trackOnly &&
|
!trackOnly &&
|
||||||
|
app?.app.installedVersion != null &&
|
||||||
app?.app.installedVersion != app?.app.latestVersion)
|
app?.app.installedVersion != app?.app.latestVersion)
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: app?.downloadProgress != null
|
onPressed: app?.downloadProgress != null
|
||||||
@@ -203,13 +206,6 @@ class _AppPageState extends State<AppPage> {
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(tr(
|
title: Text(tr(
|
||||||
'alreadyUpToDateQuestion')),
|
'alreadyUpToDateQuestion')),
|
||||||
content: Text(
|
|
||||||
tr('onlyWorksWithNonEVDApps'),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight:
|
|
||||||
FontWeight.bold,
|
|
||||||
fontStyle:
|
|
||||||
FontStyle.italic)),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -268,7 +264,8 @@ class _AppPageState extends State<AppPage> {
|
|||||||
}).toList();
|
}).toList();
|
||||||
return GeneratedFormModal(
|
return GeneratedFormModal(
|
||||||
title: tr('additionalOptions'),
|
title: tr('additionalOptions'),
|
||||||
items: items);
|
items: items,
|
||||||
|
);
|
||||||
}).then((values) {
|
}).then((values) {
|
||||||
if (app != null && values != null) {
|
if (app != null && values != null) {
|
||||||
var changedApp = app.app;
|
var changedApp = app.app;
|
||||||
@@ -289,7 +286,15 @@ class _AppPageState extends State<AppPage> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
tooltip: tr('additionalOptions'),
|
tooltip: tr('additionalOptions'),
|
||||||
icon: const Icon(Icons.settings)),
|
icon: const Icon(Icons.edit)),
|
||||||
|
if (app != null && app.installedInfo != null)
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
appsProvider.openAppSettings(app.app.id);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.settings),
|
||||||
|
tooltip: tr('settings'),
|
||||||
|
),
|
||||||
if (app != null && settingsProvider.showAppWebpage)
|
if (app != null && settingsProvider.showAppWebpage)
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -361,39 +366,11 @@ class _AppPageState extends State<AppPage> {
|
|||||||
onPressed: app?.downloadProgress != null
|
onPressed: app?.downloadProgress != null
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
showDialog(
|
appsProvider.removeAppsWithModal(
|
||||||
context: context,
|
context, [app!.app]).then((value) {
|
||||||
builder: (BuildContext ctx) {
|
if (value == true) {
|
||||||
return AlertDialog(
|
|
||||||
title: Text(tr('removeAppQuestion')),
|
|
||||||
content: Text(tr(
|
|
||||||
'xWillBeRemovedButRemainInstalled',
|
|
||||||
args: [
|
|
||||||
app?.installedInfo?.name ??
|
|
||||||
app?.app.name ??
|
|
||||||
tr('app')
|
|
||||||
])),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
HapticFeedback
|
|
||||||
.selectionClick();
|
|
||||||
appsProvider.removeApps(
|
|
||||||
[app!.app.id]).then((_) {
|
|
||||||
int count = 0;
|
|
||||||
Navigator.of(context)
|
|
||||||
.popUntil((_) =>
|
|
||||||
count++ >= 2);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Text(tr('remove'))),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
}
|
||||||
child: Text(tr('cancel')))
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
@@ -414,3 +391,18 @@ class _AppPageState extends State<AppPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RemoveAppsModal extends StatefulWidget {
|
||||||
|
const RemoveAppsModal({super.key, this.apps = const []});
|
||||||
|
final List<App> apps;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RemoveAppsModal> createState() => _RemoveAppsModalState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RemoveAppsModalState extends State<RemoveAppsModal> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Placeholder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -389,28 +389,30 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
onPressed: selectedApps.isEmpty
|
onPressed: selectedApps.isEmpty
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
showDialog<Map<String, dynamic>?>(
|
appsProvider.removeAppsWithModal(
|
||||||
context: context,
|
context, selectedApps.toList());
|
||||||
builder: (BuildContext ctx) {
|
// showDialog<Map<String, dynamic>?>(
|
||||||
return GeneratedFormModal(
|
// context: context,
|
||||||
title: tr(
|
// builder: (BuildContext ctx) {
|
||||||
'removeSelectedAppsQuestion'),
|
// return GeneratedFormModal(
|
||||||
items: const [],
|
// title: tr(
|
||||||
initValid: true,
|
// 'removeSelectedAppsQuestion'),
|
||||||
message: tr(
|
// items: const [],
|
||||||
'xWillBeRemovedButRemainInstalled',
|
// initValid: true,
|
||||||
args: [
|
// message: tr(
|
||||||
plural('apps',
|
// 'xWillBeRemovedButRemainInstalled',
|
||||||
selectedApps.length)
|
// args: [
|
||||||
]),
|
// plural('apps',
|
||||||
);
|
// selectedApps.length)
|
||||||
}).then((values) {
|
// ]),
|
||||||
if (values != null) {
|
// );
|
||||||
appsProvider.removeApps(selectedApps
|
// }).then((values) {
|
||||||
.map((e) => e.id)
|
// if (values != null) {
|
||||||
.toList());
|
// appsProvider.removeApps(selectedApps
|
||||||
}
|
// .map((e) => e.id)
|
||||||
});
|
// .toList());
|
||||||
|
// }
|
||||||
|
// });
|
||||||
},
|
},
|
||||||
tooltip: tr('removeSelectedApps'),
|
tooltip: tr('removeSelectedApps'),
|
||||||
icon: const Icon(Icons.delete_outline_outlined),
|
icon: const Icon(Icons.delete_outline_outlined),
|
||||||
@@ -660,7 +662,7 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
])),
|
])),
|
||||||
content:
|
content:
|
||||||
Text(
|
Text(
|
||||||
tr('onlyWorksWithNonEVDApps'),
|
tr('onlyWorksWithNonVersionDetectApps'),
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold, fontStyle: FontStyle.italic),
|
style: const TextStyle(fontWeight: FontWeight.bold, fontStyle: FontStyle.italic),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
@@ -673,7 +675,7 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
HapticFeedback.selectionClick();
|
HapticFeedback.selectionClick();
|
||||||
appsProvider.saveApps(selectedApps.map((a) {
|
appsProvider.saveApps(selectedApps.map((a) {
|
||||||
if (a.installedVersion != null) {
|
if (a.installedVersion != null && a.additionalSettings['noVersionDetection'] == true) {
|
||||||
a.installedVersion = a.latestVersion;
|
a.installedVersion = a.latestVersion;
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
|
@@ -63,21 +63,29 @@ class _HomePageState extends State<HomePage> {
|
|||||||
.map((e) =>
|
.map((e) =>
|
||||||
NavigationDestination(icon: Icon(e.icon), label: e.title))
|
NavigationDestination(icon: Icon(e.icon), label: e.title))
|
||||||
.toList(),
|
.toList(),
|
||||||
onDestinationSelected: (int index) {
|
onDestinationSelected: (int index) async {
|
||||||
HapticFeedback.selectionClick();
|
HapticFeedback.selectionClick();
|
||||||
setState(() {
|
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
|
while ((pages[0].widget.key as GlobalKey<AppsPageState>)
|
||||||
|
.currentState !=
|
||||||
|
null) {
|
||||||
|
// Avoid duplicate GlobalKey error
|
||||||
|
await Future.delayed(const Duration(microseconds: 1));
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
selectedIndexHistory.clear();
|
selectedIndexHistory.clear();
|
||||||
|
});
|
||||||
} else if (selectedIndexHistory.isEmpty ||
|
} else if (selectedIndexHistory.isEmpty ||
|
||||||
(selectedIndexHistory.isNotEmpty &&
|
(selectedIndexHistory.isNotEmpty &&
|
||||||
selectedIndexHistory.last != index)) {
|
selectedIndexHistory.last != index)) {
|
||||||
|
setState(() {
|
||||||
int existingInd = selectedIndexHistory.indexOf(index);
|
int existingInd = selectedIndexHistory.indexOf(index);
|
||||||
if (existingInd >= 0) {
|
if (existingInd >= 0) {
|
||||||
selectedIndexHistory.removeAt(existingInd);
|
selectedIndexHistory.removeAt(existingInd);
|
||||||
}
|
}
|
||||||
selectedIndexHistory.add(index);
|
selectedIndexHistory.add(index);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
selectedIndex:
|
selectedIndex:
|
||||||
selectedIndexHistory.isEmpty ? 0 : selectedIndexHistory.last,
|
selectedIndexHistory.isEmpty ? 0 : selectedIndexHistory.last,
|
||||||
|
@@ -564,10 +564,7 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
widget.onlyOneSelectionAllowed ? tr('selectURL') : tr('selectURLs')),
|
widget.onlyOneSelectionAllowed ? tr('selectURL') : tr('selectURLs')),
|
||||||
content: Column(children: [
|
content: Column(children: [
|
||||||
...urlWithDescriptionSelections.keys.map((urlWithD) {
|
...urlWithDescriptionSelections.keys.map((urlWithD) {
|
||||||
return Row(children: [
|
select(bool? value) {
|
||||||
Checkbox(
|
|
||||||
value: urlWithDescriptionSelections[urlWithD],
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
value ??= false;
|
value ??= false;
|
||||||
if (value! && widget.onlyOneSelectionAllowed) {
|
if (value! && widget.onlyOneSelectionAllowed) {
|
||||||
@@ -576,6 +573,13 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
urlWithDescriptionSelections[urlWithD] = value!;
|
urlWithDescriptionSelections[urlWithD] = value!;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Row(children: [
|
||||||
|
Checkbox(
|
||||||
|
value: urlWithDescriptionSelections[urlWithD],
|
||||||
|
onChanged: (value) {
|
||||||
|
select(value);
|
||||||
}),
|
}),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 8,
|
width: 8,
|
||||||
@@ -599,13 +603,18 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
const TextStyle(decoration: TextDecoration.underline),
|
const TextStyle(decoration: TextDecoration.underline),
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
)),
|
)),
|
||||||
Text(
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
select(!(urlWithDescriptionSelections[urlWithD] ?? false));
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
urlWithD.value.length > 128
|
urlWithD.value.length > 128
|
||||||
? '${urlWithD.value.substring(0, 128)}...'
|
? '${urlWithD.value.substring(0, 128)}...'
|
||||||
: urlWithD.value,
|
: urlWithD.value,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontStyle: FontStyle.italic, fontSize: 12),
|
fontStyle: FontStyle.italic, fontSize: 12),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
)
|
)
|
||||||
|
@@ -5,6 +5,7 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:android_intent_plus/flag.dart';
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -12,6 +13,8 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:install_plugin_v2/install_plugin_v2.dart';
|
import 'package:install_plugin_v2/install_plugin_v2.dart';
|
||||||
import 'package:installed_apps/app_info.dart';
|
import 'package:installed_apps/app_info.dart';
|
||||||
import 'package:installed_apps/installed_apps.dart';
|
import 'package:installed_apps/installed_apps.dart';
|
||||||
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
|
import 'package:obtainium/components/generated_form_modal.dart';
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/providers/logs_provider.dart';
|
import 'package:obtainium/providers/logs_provider.dart';
|
||||||
import 'package:obtainium/providers/notifications_provider.dart';
|
import 'package:obtainium/providers/notifications_provider.dart';
|
||||||
@@ -23,6 +26,7 @@ import 'package:path_provider/path_provider.dart';
|
|||||||
import 'package:flutter_fgbg/flutter_fgbg.dart';
|
import 'package:flutter_fgbg/flutter_fgbg.dart';
|
||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
|
import 'package:android_intent_plus/android_intent.dart';
|
||||||
|
|
||||||
class AppInMemory {
|
class AppInMemory {
|
||||||
late App app;
|
late App app;
|
||||||
@@ -247,7 +251,11 @@ class AppsProvider with ChangeNotifier {
|
|||||||
!(await canDowngradeApps())) {
|
!(await canDowngradeApps())) {
|
||||||
throw DowngradeError();
|
throw DowngradeError();
|
||||||
}
|
}
|
||||||
await InstallPlugin.installApk(file.file.path, 'dev.imranr.obtainium');
|
await InstallPlugin.installApk(file.file.path, obtainiumId);
|
||||||
|
if (file.appId == obtainiumId) {
|
||||||
|
// Obtainium prompt should be lowest
|
||||||
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
|
}
|
||||||
apps[file.appId]!.app.installedVersion =
|
apps[file.appId]!.app.installedVersion =
|
||||||
apps[file.appId]!.app.latestVersion;
|
apps[file.appId]!.app.latestVersion;
|
||||||
// Don't correct install status as installation may not be done yet
|
// Don't correct install status as installation may not be done yet
|
||||||
@@ -255,6 +263,15 @@ class AppsProvider with ChangeNotifier {
|
|||||||
attemptToCorrectInstallStatus: false);
|
attemptToCorrectInstallStatus: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uninstallApp(String appId) async {
|
||||||
|
var intent = AndroidIntent(
|
||||||
|
action: 'android.intent.action.DELETE',
|
||||||
|
data: 'package:$appId',
|
||||||
|
flags: <int>[Flag.FLAG_ACTIVITY_NEW_TASK],
|
||||||
|
package: 'vnd.android.package-archive');
|
||||||
|
await intent.launch();
|
||||||
|
}
|
||||||
|
|
||||||
Future<String?> confirmApkUrl(App app, BuildContext? context) async {
|
Future<String?> confirmApkUrl(App app, BuildContext? context) async {
|
||||||
// If the App has more than one APK, the user should pick one (if context provided)
|
// If the App has more than one APK, the user should pick one (if context provided)
|
||||||
String? apkUrl = app.apkUrls[app.preferredApkIndex];
|
String? apkUrl = app.apkUrls[app.preferredApkIndex];
|
||||||
@@ -440,9 +457,6 @@ class AppsProvider with ChangeNotifier {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
if (!res) {
|
|
||||||
logs.add(tr('versionCorrectionDisabled'));
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,6 +635,57 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> removeAppsWithModal(BuildContext context, List<App> apps) async {
|
||||||
|
var showUninstallOption =
|
||||||
|
apps.where((a) => a.installedVersion != null).isNotEmpty;
|
||||||
|
var values = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return GeneratedFormModal(
|
||||||
|
title: plural('removeAppQuestion', apps.length),
|
||||||
|
items: !showUninstallOption
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('rmAppEntry',
|
||||||
|
label: tr('removeFromObtainium'), defaultValue: true)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('uninstallApp',
|
||||||
|
label: tr('uninstallFromDevice'))
|
||||||
|
]
|
||||||
|
],
|
||||||
|
initValid: true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (values != null) {
|
||||||
|
bool uninstall = values['uninstallApp'] == true && showUninstallOption;
|
||||||
|
bool remove = values['rmAppEntry'] == true || !showUninstallOption;
|
||||||
|
if (uninstall) {
|
||||||
|
for (var i = 0; i < apps.length; i++) {
|
||||||
|
if (apps[i].installedVersion != null) {
|
||||||
|
uninstallApp(apps[i].id);
|
||||||
|
apps[i].installedVersion = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await saveApps(apps, attemptToCorrectInstallStatus: false);
|
||||||
|
}
|
||||||
|
if (remove) {
|
||||||
|
await removeApps(apps.map((e) => e.id).toList());
|
||||||
|
}
|
||||||
|
return uninstall || remove;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> openAppSettings(String appId) async {
|
||||||
|
final AndroidIntent intent = AndroidIntent(
|
||||||
|
action: 'action_application_details_settings',
|
||||||
|
data: 'package:$appId',
|
||||||
|
);
|
||||||
|
await intent.launch();
|
||||||
|
}
|
||||||
|
|
||||||
Future<App?> checkUpdate(String appId) async {
|
Future<App?> checkUpdate(String appId) async {
|
||||||
App? currentApp = apps[appId]!.app;
|
App? currentApp = apps[appId]!.app;
|
||||||
SourceProvider sourceProvider = SourceProvider();
|
SourceProvider sourceProvider = SourceProvider();
|
||||||
@@ -706,7 +771,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
exportDir = await getExternalStorageDirectory();
|
exportDir = await getExternalStorageDirectory();
|
||||||
path = exportDir!.path;
|
path = exportDir!.path;
|
||||||
}
|
}
|
||||||
if ((await DeviceInfoPlugin().androidInfo).version.sdkInt <= 28) {
|
if ((await DeviceInfoPlugin().androidInfo).version.sdkInt <= 29) {
|
||||||
if (await Permission.storage.isDenied) {
|
if (await Permission.storage.isDenied) {
|
||||||
await Permission.storage.request();
|
await Permission.storage.request();
|
||||||
}
|
}
|
||||||
|
32
pubspec.lock
@@ -9,6 +9,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
android_intent_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: android_intent_plus
|
||||||
|
sha256: ebd110b60723334bdc6eeb373116d6c52e9bed8feb9dcbd9f034531f56636e31
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.5"
|
||||||
animations:
|
animations:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -101,10 +109,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: f71079978789bc2fe78d79227f1f8cfe195b31bbd8db2399b0d15a4b96fb843b
|
sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3+2"
|
version: "0.3.3+4"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -157,10 +165,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dynamic_color
|
name: dynamic_color
|
||||||
sha256: "37a15576f5a0bfd5555b613cf20ea3bd379607cf88d457374a16032f4e942174"
|
sha256: c4a508284b14ec4dda5adba2c28b2cdd34fbae1afead7e8c52cad87d51c5405b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.4"
|
version: "1.6.2"
|
||||||
easy_localization:
|
easy_localization:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -609,10 +617,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_foundation
|
name: shared_preferences_foundation
|
||||||
sha256: "1ffa239043ab8baf881ec3094a3c767af9d10399b2839020b9e4d44c0bb23951"
|
sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
shared_preferences_linux:
|
shared_preferences_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -830,10 +838,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_android
|
name: webview_flutter_android
|
||||||
sha256: "9d97fa2bae0f1900553c48a2ef0aaa3864367fd7bb625d683c460754b691312c"
|
sha256: "5f49a6e5fc59e21fcec5e1bbcd401afbee9792a24a4f3d9cef9b5bb0cd1e3767"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.1"
|
version: "3.2.4"
|
||||||
webview_flutter_platform_interface:
|
webview_flutter_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -846,10 +854,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_wkwebview
|
name: webview_flutter_wkwebview
|
||||||
sha256: "523aff9168af9bb2170e4809e0499d7dee065c3919799fd3341d3e616c137960"
|
sha256: "92e7e7fa468f1df597fb9d37bcf1f303175cbe147c4dbdf06ecc323d950116eb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.5"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -883,5 +891,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.18.2 <4.0.0"
|
dart: ">=2.18.2 <3.0.0"
|
||||||
flutter: ">=3.3.0"
|
flutter: ">=3.4.0-17.0.pre"
|
||||||
|
@@ -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.10.5+111 # When changing this, update the tag in main() accordingly
|
version: 0.10.7+113 # When changing this, update the tag in main() accordingly
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.18.2 <3.0.0'
|
sdk: '>=2.18.2 <3.0.0'
|
||||||
@@ -58,6 +58,7 @@ dependencies:
|
|||||||
android_alarm_manager_plus: ^2.1.0
|
android_alarm_manager_plus: ^2.1.0
|
||||||
sqflite: ^2.2.0+3
|
sqflite: ^2.2.0+3
|
||||||
easy_localization: ^3.0.1
|
easy_localization: ^3.0.1
|
||||||
|
android_intent_plus: ^3.1.5
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|