mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-08-22 14:09:29 +02:00
Merge pull request #1161 from ImranR98/dev
- Custom link support (#368, #918) - Export settings (#1157) - Use public GitLab search API (#1147) - Fix unauthorized error (#1153)
This commit is contained in:
@@ -28,8 +28,15 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action
|
<action
|
||||||
android:name="com.android_package_installer.content.SESSION_API_PACKAGE_INSTALLED"
|
android:name="com.android_package_installer.content.SESSION_API_PACKAGE_INSTALLED"
|
||||||
android:exported="false"/>
|
android:exported="false" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="obtainium" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
@@ -39,10 +46,10 @@
|
|||||||
<service
|
<service
|
||||||
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
|
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||||
android:exported="false"/>
|
android:exported="false" />
|
||||||
<receiver
|
<receiver
|
||||||
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
|
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
|
||||||
android:exported="false"/>
|
android:exported="false" />
|
||||||
<receiver
|
<receiver
|
||||||
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
|
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
@@ -52,24 +59,24 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="dev.imranr.obtainium"
|
android:authorities="dev.imranr.obtainium"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/file_paths"/>
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<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 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="29"/>
|
android:maxSdkVersion="29" />
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||||
</manifest>
|
</manifest>
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Istaknite manje vidljive touch mete",
|
"highlightTouchTargets": "Istaknite manje vidljive touch mete",
|
||||||
"pickExportDir": "Izaberite datoteku za izvoz",
|
"pickExportDir": "Izaberite datoteku za izvoz",
|
||||||
"autoExportOnChanges": "Automatski izvezite pri promjenama",
|
"autoExportOnChanges": "Automatski izvezite pri promjenama",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filtrirajte verzije po regulatnom izrazu",
|
"filterVersionsByRegEx": "Filtrirajte verzije po regulatnom izrazu",
|
||||||
"trySelectingSuggestedVersionCode": "Probajte izabrati preloženu (verziju) versionCode APK-a",
|
"trySelectingSuggestedVersionCode": "Probajte izabrati preloženu (verziju) versionCode APK-a",
|
||||||
"dontSortReleasesList": "Zadrži redosled izdanja iz API-a",
|
"dontSortReleasesList": "Zadrži redosled izdanja iz API-a",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
|
"highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
|
||||||
"pickExportDir": "Vybrat adresář pro export",
|
"pickExportDir": "Vybrat adresář pro export",
|
||||||
"autoExportOnChanges": "Automatický export při změnách",
|
"autoExportOnChanges": "Automatický export při změnách",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu",
|
"filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu",
|
||||||
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
|
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben",
|
"highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben",
|
||||||
"pickExportDir": "Export-Verzeichnis wählen",
|
"pickExportDir": "Export-Verzeichnis wählen",
|
||||||
"autoExportOnChanges": "Automatischer Export bei Änderung(en)",
|
"autoExportOnChanges": "Automatischer Export bei Änderung(en)",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
|
"filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
|
||||||
"trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
|
"trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
|
||||||
"dontSortReleasesList": "Freigaberelease von der API ordern",
|
"dontSortReleasesList": "Freigaberelease von der API ordern",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"highlightTouchTargets": "Highlight less obvious touch targets",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"pickExportDir": "Pick Export Directory",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"autoExportOnChanges": "Auto-export on changes",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Resaltar objetivos menos obvios",
|
"highlightTouchTargets": "Resaltar objetivos menos obvios",
|
||||||
"pickExportDir": "Selecciona el Directorio para Exportar",
|
"pickExportDir": "Selecciona el Directorio para Exportar",
|
||||||
"autoExportOnChanges": "Auto Exportar cuando haya cambios",
|
"autoExportOnChanges": "Auto Exportar cuando haya cambios",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filtrar por Versiones",
|
"filterVersionsByRegEx": "Filtrar por Versiones",
|
||||||
"trySelectingSuggestedVersionCode": "Prueba seleccionando la versionCode APK sugerida",
|
"trySelectingSuggestedVersionCode": "Prueba seleccionando la versionCode APK sugerida",
|
||||||
"dontSortReleasesList": "Mantener el order de publicación desde API",
|
"dontSortReleasesList": "Mantener el order de publicación desde API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "اهداف لمسی کمتر واضح را برجسته کنید",
|
"highlightTouchTargets": "اهداف لمسی کمتر واضح را برجسته کنید",
|
||||||
"pickExportDir": "فهرست صادرات را انتخاب کنید",
|
"pickExportDir": "فهرست صادرات را انتخاب کنید",
|
||||||
"autoExportOnChanges": "صادرات خودکار تغییرات",
|
"autoExportOnChanges": "صادرات خودکار تغییرات",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "فیلتر کردن نسخه ها با RegEx",
|
"filterVersionsByRegEx": "فیلتر کردن نسخه ها با RegEx",
|
||||||
"trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید",
|
"trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید",
|
||||||
"dontSortReleasesList": "حفظ سفارش انتشار از API",
|
"dontSortReleasesList": "حفظ سفارش انتشار از API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"highlightTouchTargets": "Highlight less obvious touch targets",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"pickExportDir": "Pick Export Directory",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"autoExportOnChanges": "Auto-export on changes",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
|
@@ -255,6 +255,7 @@
|
|||||||
"highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
|
"highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
|
||||||
"pickExportDir": "Válassza az Exportálási könyvtárat",
|
"pickExportDir": "Válassza az Exportálási könyvtárat",
|
||||||
"autoExportOnChanges": "Auto-exportálás a változások után",
|
"autoExportOnChanges": "Auto-exportálás a változások után",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
|
"filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
|
||||||
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
|
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
|
||||||
"dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
|
"dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi",
|
"highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi",
|
||||||
"pickExportDir": "Scegli cartella esp.",
|
"pickExportDir": "Scegli cartella esp.",
|
||||||
"autoExportOnChanges": "Auto-esporta dopo modifiche",
|
"autoExportOnChanges": "Auto-esporta dopo modifiche",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filtra versioni con espressione regolare",
|
"filterVersionsByRegEx": "Filtra versioni con espressione regolare",
|
||||||
"trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito",
|
"trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito",
|
||||||
"dontSortReleasesList": "Conserva l'ordine di release da API",
|
"dontSortReleasesList": "Conserva l'ordine di release da API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
|
"highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
|
||||||
"pickExportDir": "エクスポートディレクトリを選択",
|
"pickExportDir": "エクスポートディレクトリを選択",
|
||||||
"autoExportOnChanges": "変更があった際に自動でエクスポートする",
|
"autoExportOnChanges": "変更があった際に自動でエクスポートする",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
|
"filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
|
||||||
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
|
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
|
||||||
"dontSortReleasesList": "APIからのリリース順を保持する",
|
"dontSortReleasesList": "APIからのリリース順を保持する",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.",
|
"highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.",
|
||||||
"pickExportDir": "Kies de exportmap",
|
"pickExportDir": "Kies de exportmap",
|
||||||
"autoExportOnChanges": "Automatisch exporteren bij wijzigingen",
|
"autoExportOnChanges": "Automatisch exporteren bij wijzigingen",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filter versies met een reguliere expressie",
|
"filterVersionsByRegEx": "Filter versies met een reguliere expressie",
|
||||||
"trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
|
"trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
|
||||||
"dontSortReleasesList": "Volgorde van releases behouden vanuit de API",
|
"dontSortReleasesList": "Volgorde van releases behouden vanuit de API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
|
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
|
||||||
"pickExportDir": "Wybierz katalog eksportu",
|
"pickExportDir": "Wybierz katalog eksportu",
|
||||||
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
|
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
|
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
|
||||||
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
|
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
|
||||||
"dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
|
"dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Destaque areas de toque menos óbvias",
|
"highlightTouchTargets": "Destaque areas de toque menos óbvias",
|
||||||
"pickExportDir": "Escolher Diretorio de Exportação",
|
"pickExportDir": "Escolher Diretorio de Exportação",
|
||||||
"autoExportOnChanges": "Auto-exportar em mudanças",
|
"autoExportOnChanges": "Auto-exportar em mudanças",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filtrar Versões por Expressão Regular",
|
"filterVersionsByRegEx": "Filtrar Versões por Expressão Regular",
|
||||||
"trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
|
"trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
|
||||||
"dontSortReleasesList": "Reter a ordem de lançamento da API",
|
"dontSortReleasesList": "Reter a ordem de lançamento da API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
|
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
|
||||||
"pickExportDir": "Выбрать каталог для экспорта",
|
"pickExportDir": "Выбрать каталог для экспорта",
|
||||||
"autoExportOnChanges": "Автоэкспорт при изменениях",
|
"autoExportOnChanges": "Автоэкспорт при изменениях",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
|
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
|
||||||
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
|
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
|
||||||
"dontSortReleasesList": "Сохранить порядок релизов от API",
|
"dontSortReleasesList": "Сохранить порядок релизов от API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"highlightTouchTargets": "Highlight less obvious touch targets",
|
||||||
"pickExportDir": "Välj Exportsökväg",
|
"pickExportDir": "Välj Exportsökväg",
|
||||||
"autoExportOnChanges": "Automatisk export vid ändringar",
|
"autoExportOnChanges": "Automatisk export vid ändringar",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula",
|
"highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula",
|
||||||
"pickExportDir": "Dışa Aktarılacak Klasörü Seç",
|
"pickExportDir": "Dışa Aktarılacak Klasörü Seç",
|
||||||
"autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar",
|
"autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele",
|
"filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele",
|
||||||
"trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
|
"trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
|
||||||
"dontSortReleasesList": "API'den sıralama düzenini koru",
|
"dontSortReleasesList": "API'den sıralama düzenini koru",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn",
|
"highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn",
|
||||||
"pickExportDir": "Chọn thư mục xuất",
|
"pickExportDir": "Chọn thư mục xuất",
|
||||||
"autoExportOnChanges": "Tự động xuất khi thay đổi",
|
"autoExportOnChanges": "Tự động xuất khi thay đổi",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Lọc phiên bản theo biểu thức chính quy",
|
"filterVersionsByRegEx": "Lọc phiên bản theo biểu thức chính quy",
|
||||||
"trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
|
"trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
|
||||||
"dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
|
"dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
|
||||||
|
@@ -256,6 +256,7 @@
|
|||||||
"highlightTouchTargets": "突出展示不明显的触摸区域",
|
"highlightTouchTargets": "突出展示不明显的触摸区域",
|
||||||
"pickExportDir": "选择导出文件夹",
|
"pickExportDir": "选择导出文件夹",
|
||||||
"autoExportOnChanges": "数据变更时自动导出",
|
"autoExportOnChanges": "数据变更时自动导出",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "筛选版本号(正则表达式)",
|
"filterVersionsByRegEx": "筛选版本号(正则表达式)",
|
||||||
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
|
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
|
||||||
"dontSortReleasesList": "保持来自 API 的发行顺序",
|
"dontSortReleasesList": "保持来自 API 的发行顺序",
|
||||||
|
@@ -48,12 +48,6 @@ 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
|
||||||
@@ -86,18 +80,8 @@ 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;
|
|
||||||
if (!hostChanged) {
|
|
||||||
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?${PAT?.isNotEmpty == true ? 'private_token=$PAT&' : ''}scope=projects&search=${Uri.encodeQueryComponent(query)}';
|
'https://$host/api/v4/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);
|
||||||
|
@@ -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.36';
|
const String currentVersion = '0.14.37';
|
||||||
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
|
||||||
|
|
||||||
|
@@ -21,10 +21,10 @@ class AddAppPage extends StatefulWidget {
|
|||||||
const AddAppPage({super.key});
|
const AddAppPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AddAppPage> createState() => _AddAppPageState();
|
State<AddAppPage> createState() => AddAppPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AddAppPageState extends State<AddAppPage> {
|
class AddAppPageState extends State<AddAppPage> {
|
||||||
bool gettingAppInfo = false;
|
bool gettingAppInfo = false;
|
||||||
bool searching = false;
|
bool searching = false;
|
||||||
|
|
||||||
@@ -36,9 +36,62 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
bool additionalSettingsValid = true;
|
bool additionalSettingsValid = true;
|
||||||
bool inferAppIdIfOptional = true;
|
bool inferAppIdIfOptional = true;
|
||||||
List<String> pickedCategories = [];
|
List<String> pickedCategories = [];
|
||||||
int searchnum = 0;
|
int urlInputKey = 0;
|
||||||
SourceProvider sourceProvider = SourceProvider();
|
SourceProvider sourceProvider = SourceProvider();
|
||||||
|
|
||||||
|
linkFn(String input) {
|
||||||
|
try {
|
||||||
|
if (input.isEmpty) {
|
||||||
|
throw UnsupportedURLError();
|
||||||
|
}
|
||||||
|
sourceProvider.getSource(input);
|
||||||
|
changeUserInput(input, true, false, updateUrlInput: true);
|
||||||
|
} catch (e) {
|
||||||
|
showError(e, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeUserInput(String input, bool valid, bool isBuilding,
|
||||||
|
{bool updateUrlInput = false}) {
|
||||||
|
userInput = input;
|
||||||
|
if (!isBuilding) {
|
||||||
|
setState(() {
|
||||||
|
if (updateUrlInput) {
|
||||||
|
urlInputKey++;
|
||||||
|
}
|
||||||
|
var prevHost = pickedSource?.host;
|
||||||
|
try {
|
||||||
|
var naturalSource =
|
||||||
|
valid ? sourceProvider.getSource(userInput) : null;
|
||||||
|
if (naturalSource != null &&
|
||||||
|
naturalSource.runtimeType.toString() !=
|
||||||
|
HTML().runtimeType.toString()) {
|
||||||
|
// If input has changed to match a regular source, reset the override
|
||||||
|
pickedSourceOverride = null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
var source = valid
|
||||||
|
? sourceProvider.getSource(userInput,
|
||||||
|
overrideSource: pickedSourceOverride)
|
||||||
|
: null;
|
||||||
|
if (pickedSource.runtimeType != source.runtimeType ||
|
||||||
|
(prevHost != null && prevHost != source?.host)) {
|
||||||
|
pickedSource = source;
|
||||||
|
additionalSettings = source != null
|
||||||
|
? getDefaultValuesFromFormItems(
|
||||||
|
source.combinedAppSpecificSettingFormItems)
|
||||||
|
: {};
|
||||||
|
additionalSettingsValid = source != null
|
||||||
|
? !sourceProvider.ifRequiredAppSpecificSettingsExist(source)
|
||||||
|
: true;
|
||||||
|
inferAppIdIfOptional = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
AppsProvider appsProvider = context.read<AppsProvider>();
|
AppsProvider appsProvider = context.read<AppsProvider>();
|
||||||
@@ -48,47 +101,6 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
|
|
||||||
bool doingSomething = gettingAppInfo || searching;
|
bool doingSomething = gettingAppInfo || searching;
|
||||||
|
|
||||||
changeUserInput(String input, bool valid, bool isBuilding,
|
|
||||||
{bool isSearch = false}) {
|
|
||||||
userInput = input;
|
|
||||||
if (!isBuilding) {
|
|
||||||
setState(() {
|
|
||||||
if (isSearch) {
|
|
||||||
searchnum++;
|
|
||||||
}
|
|
||||||
var prevHost = pickedSource?.host;
|
|
||||||
try {
|
|
||||||
var naturalSource =
|
|
||||||
valid ? sourceProvider.getSource(userInput) : null;
|
|
||||||
if (naturalSource != null &&
|
|
||||||
naturalSource.runtimeType.toString() !=
|
|
||||||
HTML().runtimeType.toString()) {
|
|
||||||
// If input has changed to match a regular source, reset the override
|
|
||||||
pickedSourceOverride = null;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
var source = valid
|
|
||||||
? sourceProvider.getSource(userInput,
|
|
||||||
overrideSource: pickedSourceOverride)
|
|
||||||
: null;
|
|
||||||
if (pickedSource.runtimeType != source.runtimeType ||
|
|
||||||
(prevHost != null && prevHost != source?.host)) {
|
|
||||||
pickedSource = source;
|
|
||||||
additionalSettings = source != null
|
|
||||||
? getDefaultValuesFromFormItems(
|
|
||||||
source.combinedAppSpecificSettingFormItems)
|
|
||||||
: {};
|
|
||||||
additionalSettingsValid = source != null
|
|
||||||
? !sourceProvider.ifRequiredAppSpecificSettingsExist(source)
|
|
||||||
: true;
|
|
||||||
inferAppIdIfOptional = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> getTrackOnlyConfirmationIfNeeded(bool userPickedTrackOnly,
|
Future<bool> getTrackOnlyConfirmationIfNeeded(bool userPickedTrackOnly,
|
||||||
{bool ignoreHideSetting = false}) async {
|
{bool ignoreHideSetting = false}) async {
|
||||||
var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly;
|
var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly;
|
||||||
@@ -205,7 +217,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GeneratedForm(
|
child: GeneratedForm(
|
||||||
key: Key(searchnum.toString()),
|
key: Key(urlInputKey.toString()),
|
||||||
items: [
|
items: [
|
||||||
[
|
[
|
||||||
GeneratedFormTextField('appSourceURL',
|
GeneratedFormTextField('appSourceURL',
|
||||||
@@ -325,7 +337,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (selectedUrls != null && selectedUrls.isNotEmpty) {
|
if (selectedUrls != null && selectedUrls.isNotEmpty) {
|
||||||
changeUserInput(selectedUrls[0], true, false, isSearch: true);
|
changeUserInput(selectedUrls[0], true, false, updateUrlInput: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:animations/animations.dart';
|
import 'package:animations/animations.dart';
|
||||||
|
import 'package:app_links/app_links.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';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/pages/add_app.dart';
|
import 'package:obtainium/pages/add_app.dart';
|
||||||
import 'package:obtainium/pages/apps.dart';
|
import 'package:obtainium/pages/apps.dart';
|
||||||
import 'package:obtainium/pages/import_export.dart';
|
import 'package:obtainium/pages/import_export.dart';
|
||||||
@@ -30,58 +34,119 @@ class _HomePageState extends State<HomePage> {
|
|||||||
bool isReversing = false;
|
bool isReversing = false;
|
||||||
int prevAppCount = -1;
|
int prevAppCount = -1;
|
||||||
bool prevIsLoading = true;
|
bool prevIsLoading = true;
|
||||||
|
late AppLinks _appLinks;
|
||||||
|
StreamSubscription<Uri>? _linkSubscription;
|
||||||
|
bool isLinkActivity = false;
|
||||||
|
|
||||||
List<NavigationPageItem> pages = [
|
List<NavigationPageItem> pages = [
|
||||||
NavigationPageItem(tr('appsString'), Icons.apps,
|
NavigationPageItem(tr('appsString'), Icons.apps,
|
||||||
AppsPage(key: GlobalKey<AppsPageState>())),
|
AppsPage(key: GlobalKey<AppsPageState>())),
|
||||||
NavigationPageItem(tr('addApp'), Icons.add, const AddAppPage()),
|
NavigationPageItem(
|
||||||
|
tr('addApp'), Icons.add, AddAppPage(key: GlobalKey<AddAppPageState>())),
|
||||||
NavigationPageItem(
|
NavigationPageItem(
|
||||||
tr('importExport'), Icons.import_export, const ImportExportPage()),
|
tr('importExport'), Icons.import_export, const ImportExportPage()),
|
||||||
NavigationPageItem(tr('settings'), Icons.settings, const SettingsPage())
|
NavigationPageItem(tr('settings'), Icons.settings, const SettingsPage())
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
initDeepLinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initDeepLinks() async {
|
||||||
|
_appLinks = AppLinks();
|
||||||
|
|
||||||
|
goToAddApp(String data) async {
|
||||||
|
switchToPage(1);
|
||||||
|
while (
|
||||||
|
(pages[1].widget.key as GlobalKey<AddAppPageState>?)?.currentState ==
|
||||||
|
null) {
|
||||||
|
await Future.delayed(const Duration(microseconds: 1));
|
||||||
|
}
|
||||||
|
(pages[1].widget.key as GlobalKey<AddAppPageState>?)
|
||||||
|
?.currentState
|
||||||
|
?.linkFn(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
interpretLink(Uri uri) async {
|
||||||
|
isLinkActivity = true;
|
||||||
|
var action = uri.host;
|
||||||
|
var data = uri.path.length > 1 ? uri.path.substring(1) : "";
|
||||||
|
try {
|
||||||
|
if (action == 'add') {
|
||||||
|
await goToAddApp(data);
|
||||||
|
} else if (action == 'app') {
|
||||||
|
await context
|
||||||
|
.read<AppsProvider>()
|
||||||
|
.import('{ "apps": [${Uri.decodeComponent(data)}] }');
|
||||||
|
} else if (action == 'apps') {
|
||||||
|
await context
|
||||||
|
.read<AppsProvider>()
|
||||||
|
.import('{ "apps": ${Uri.decodeComponent(data)} }');
|
||||||
|
} else {
|
||||||
|
throw ObtainiumError(tr('unknown'));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showError(e, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check initial link if app was in cold state (terminated)
|
||||||
|
final appLink = await _appLinks.getInitialAppLink();
|
||||||
|
if (appLink != null) {
|
||||||
|
await interpretLink(appLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle link when app is in warm state (front or background)
|
||||||
|
_linkSubscription = _appLinks.uriLinkStream.listen((uri) async {
|
||||||
|
await interpretLink(uri);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsReversing(int targetIndex) {
|
||||||
|
bool reversing = selectedIndexHistory.isNotEmpty &&
|
||||||
|
selectedIndexHistory.last > targetIndex;
|
||||||
|
setState(() {
|
||||||
|
isReversing = reversing;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
switchToPage(int index) async {
|
||||||
|
setIsReversing(index);
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
} else if (selectedIndexHistory.isEmpty ||
|
||||||
|
(selectedIndexHistory.isNotEmpty &&
|
||||||
|
selectedIndexHistory.last != index)) {
|
||||||
|
setState(() {
|
||||||
|
int existingInd = selectedIndexHistory.indexOf(index);
|
||||||
|
if (existingInd >= 0) {
|
||||||
|
selectedIndexHistory.removeAt(existingInd);
|
||||||
|
}
|
||||||
|
selectedIndexHistory.add(index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
AppsProvider appsProvider = context.watch<AppsProvider>();
|
AppsProvider appsProvider = context.watch<AppsProvider>();
|
||||||
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
||||||
|
|
||||||
setIsReversing(int targetIndex) {
|
|
||||||
bool reversing = selectedIndexHistory.isNotEmpty &&
|
|
||||||
selectedIndexHistory.last > targetIndex;
|
|
||||||
setState(() {
|
|
||||||
isReversing = reversing;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
switchToPage(int index) async {
|
|
||||||
setIsReversing(index);
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
} else if (selectedIndexHistory.isEmpty ||
|
|
||||||
(selectedIndexHistory.isNotEmpty &&
|
|
||||||
selectedIndexHistory.last != index)) {
|
|
||||||
setState(() {
|
|
||||||
int existingInd = selectedIndexHistory.indexOf(index);
|
|
||||||
if (existingInd >= 0) {
|
|
||||||
selectedIndexHistory.removeAt(existingInd);
|
|
||||||
}
|
|
||||||
selectedIndexHistory.add(index);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prevIsLoading &&
|
if (!prevIsLoading &&
|
||||||
prevAppCount >= 0 &&
|
prevAppCount >= 0 &&
|
||||||
appsProvider.apps.length > prevAppCount &&
|
appsProvider.apps.length > prevAppCount &&
|
||||||
selectedIndexHistory.isNotEmpty &&
|
selectedIndexHistory.isNotEmpty &&
|
||||||
selectedIndexHistory.last == 1) {
|
selectedIndexHistory.last == 1 &&
|
||||||
|
!isLinkActivity) {
|
||||||
switchToPage(0);
|
switchToPage(0);
|
||||||
}
|
}
|
||||||
prevAppCount = appsProvider.apps.length;
|
prevAppCount = appsProvider.apps.length;
|
||||||
@@ -129,6 +194,11 @@ class _HomePageState extends State<HomePage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
|
if (isLinkActivity &&
|
||||||
|
selectedIndexHistory.length == 1 &&
|
||||||
|
selectedIndexHistory.last == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
setIsReversing(selectedIndexHistory.length >= 2
|
setIsReversing(selectedIndexHistory.length >= 2
|
||||||
? selectedIndexHistory.reversed.toList()[1]
|
? selectedIndexHistory.reversed.toList()[1]
|
||||||
: 0);
|
: 0);
|
||||||
@@ -143,4 +213,10 @@ class _HomePageState extends State<HomePage> {
|
|||||||
?.clearSelected();
|
?.clearSelected();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_linkSubscription?.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -106,7 +106,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
runObtainiumExport({bool pickOnly = false}) async {
|
runObtainiumExport({bool pickOnly = false}) async {
|
||||||
HapticFeedback.selectionClick();
|
HapticFeedback.selectionClick();
|
||||||
appsProvider
|
appsProvider
|
||||||
.exportApps(
|
.export(
|
||||||
pickOnly:
|
pickOnly:
|
||||||
pickOnly || (await settingsProvider.getExportDir()) == null,
|
pickOnly || (await settingsProvider.getExportDir()) == null,
|
||||||
sp: settingsProvider)
|
sp: settingsProvider)
|
||||||
@@ -132,7 +132,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw ObtainiumError(tr('invalidInput'));
|
throw ObtainiumError(tr('invalidInput'));
|
||||||
}
|
}
|
||||||
appsProvider.importApps(data).then((value) {
|
appsProvider.import(data).then((value) {
|
||||||
var cats = settingsProvider.categories;
|
var cats = settingsProvider.categories;
|
||||||
appsProvider.apps.forEach((key, value) {
|
appsProvider.apps.forEach((key, value) {
|
||||||
for (var c in value.app.categories) {
|
for (var c in value.app.categories) {
|
||||||
@@ -143,7 +143,10 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
});
|
});
|
||||||
appsProvider.addMissingCategories(settingsProvider);
|
appsProvider.addMissingCategories(settingsProvider);
|
||||||
showMessage(
|
showMessage(
|
||||||
tr('importedX', args: [plural('apps', value)]), context);
|
'${tr('importedX', args: [
|
||||||
|
plural('apps', value.key)
|
||||||
|
])}${value.value ? ' + ${tr('settings')}' : ''}',
|
||||||
|
context);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// User canceled the picker
|
// User canceled the picker
|
||||||
@@ -388,6 +391,14 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
defaultValue: settingsProvider
|
defaultValue: settingsProvider
|
||||||
.autoExportOnChanges,
|
.autoExportOnChanges,
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch(
|
||||||
|
'exportSettings',
|
||||||
|
label: tr('includeSettings'),
|
||||||
|
defaultValue: settingsProvider
|
||||||
|
.exportSettings,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
onValueChanges:
|
onValueChanges:
|
||||||
@@ -400,6 +411,12 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
'autoExportOnChanges'] ==
|
'autoExportOnChanges'] ==
|
||||||
true;
|
true;
|
||||||
}
|
}
|
||||||
|
if (value['exportSettings'] !=
|
||||||
|
null) {
|
||||||
|
settingsProvider.exportSettings =
|
||||||
|
value['exportSettings'] ==
|
||||||
|
true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@@ -974,7 +974,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
exportApps(isAuto: true);
|
export(isAuto: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeApps(List<String> appIds) async {
|
Future<void> removeApps(List<String> appIds) async {
|
||||||
@@ -996,7 +996,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
if (appIds.isNotEmpty) {
|
if (appIds.isNotEmpty) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
exportApps(isAuto: true);
|
export(isAuto: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1173,7 +1173,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
return updateAppIds;
|
return updateAppIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> exportApps(
|
Future<String?> export(
|
||||||
{bool pickOnly = false, isAuto = false, SettingsProvider? sp}) async {
|
{bool pickOnly = false, isAuto = false, SettingsProvider? sp}) async {
|
||||||
SettingsProvider settingsProvider = sp ?? this.settingsProvider;
|
SettingsProvider settingsProvider = sp ?? this.settingsProvider;
|
||||||
var exportDir = await settingsProvider.getExportDir();
|
var exportDir = await settingsProvider.getExportDir();
|
||||||
@@ -1203,12 +1203,22 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
String? returnPath;
|
String? returnPath;
|
||||||
if (!pickOnly) {
|
if (!pickOnly) {
|
||||||
|
Map<String, dynamic> finalExport = {};
|
||||||
|
finalExport['apps'] = apps.values.map((e) => e.app.toJson()).toList();
|
||||||
|
if (settingsProvider.exportSettings) {
|
||||||
|
finalExport['settings'] = Map<String, Object?>.fromEntries(
|
||||||
|
(settingsProvider.prefs
|
||||||
|
?.getKeys()
|
||||||
|
.map((key) =>
|
||||||
|
MapEntry(key, settingsProvider.prefs?.get(key)))
|
||||||
|
.toList()) ??
|
||||||
|
[]);
|
||||||
|
}
|
||||||
var result = await saf.createFile(exportDir,
|
var result = await saf.createFile(exportDir,
|
||||||
displayName:
|
displayName:
|
||||||
'${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().toIso8601String().replaceAll(':', '-')}${isAuto ? '-auto' : ''}.json',
|
'${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().toIso8601String().replaceAll(':', '-')}${isAuto ? '-auto' : ''}.json',
|
||||||
mimeType: 'application/json',
|
mimeType: 'application/json',
|
||||||
bytes: Uint8List.fromList(utf8.encode(
|
bytes: Uint8List.fromList(utf8.encode(jsonEncode(finalExport))));
|
||||||
jsonEncode(apps.values.map((e) => e.app.toJson()).toList()))));
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw ObtainiumError(tr('unexpectedError'));
|
throw ObtainiumError(tr('unexpectedError'));
|
||||||
}
|
}
|
||||||
@@ -1218,10 +1228,13 @@ class AppsProvider with ChangeNotifier {
|
|||||||
return returnPath;
|
return returnPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> importApps(String appsJSON) async {
|
Future<MapEntry<int, bool>> import(String appsJSON) async {
|
||||||
List<App> importedApps = (jsonDecode(appsJSON) as List<dynamic>)
|
var decodedJSON = jsonDecode(appsJSON);
|
||||||
.map((e) => App.fromJson(e))
|
var newFormat = !(decodedJSON is List);
|
||||||
.toList();
|
List<App> importedApps =
|
||||||
|
((newFormat ? decodedJSON['apps'] : decodedJSON) as List<dynamic>)
|
||||||
|
.map((e) => App.fromJson(e))
|
||||||
|
.toList();
|
||||||
while (loadingApps) {
|
while (loadingApps) {
|
||||||
await Future.delayed(const Duration(microseconds: 1));
|
await Future.delayed(const Duration(microseconds: 1));
|
||||||
}
|
}
|
||||||
@@ -1232,7 +1245,20 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
await saveApps(importedApps, onlyIfExists: false);
|
await saveApps(importedApps, onlyIfExists: false);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return importedApps.length;
|
if (newFormat && decodedJSON['settings'] != null) {
|
||||||
|
var settingsMap = decodedJSON['settings'] as Map<String, Object?>;
|
||||||
|
settingsMap.forEach((key, value) {
|
||||||
|
if (value is int) {
|
||||||
|
settingsProvider.prefs?.setInt(key, value);
|
||||||
|
} else if (value is bool) {
|
||||||
|
settingsProvider.prefs?.setBool(key, value);
|
||||||
|
} else {
|
||||||
|
settingsProvider.prefs?.setString(key, value as String);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return MapEntry<int, bool>(
|
||||||
|
importedApps.length, newFormat && decodedJSON['settings'] != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@@ -213,7 +213,8 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String? getSettingString(String settingId) {
|
String? getSettingString(String settingId) {
|
||||||
return prefs?.getString(settingId);
|
String? str = prefs?.getString(settingId);
|
||||||
|
return str?.isNotEmpty == true ? str : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSettingString(String settingId, String value) {
|
void setSettingString(String settingId, String value) {
|
||||||
@@ -415,4 +416,13 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val);
|
prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get exportSettings {
|
||||||
|
return prefs?.getBool('exportSettings') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set exportSettings(bool val) {
|
||||||
|
prefs?.setBool('exportSettings', val);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
44
pubspec.lock
44
pubspec.lock
@@ -42,6 +42,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.10"
|
version: "2.0.10"
|
||||||
|
app_links:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: app_links
|
||||||
|
sha256: "4e392b5eba997df356ca6021f28431ce1cfeb16758699553a94b13add874a3bb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.5.0"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -94,10 +102,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cli_util
|
name: cli_util
|
||||||
sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7
|
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.0"
|
version: "0.4.1"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -350,6 +358,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.2.4"
|
version: "8.2.4"
|
||||||
|
gtk:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: gtk
|
||||||
|
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
hsluv:
|
hsluv:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -807,10 +823,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
|
sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.1"
|
version: "6.2.2"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -831,10 +847,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_linux
|
name: url_launcher_linux
|
||||||
sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
|
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.1"
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -855,26 +871,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7"
|
sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.2"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_windows
|
name: url_launcher_windows
|
||||||
sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
|
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.1"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921
|
sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.1"
|
version: "4.2.2"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -903,10 +919,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_android
|
name: webview_flutter_android
|
||||||
sha256: "8326ee235f87605a2bfc444a4abc897f4abc78d83f054ba7d3d1074ce82b4fbf"
|
sha256: e313dcdf45d4c95bcb8960351ef2389b7f0687b90bc92483f7f7983ae5758456
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.12.1"
|
version: "3.13.0"
|
||||||
webview_flutter_platform_interface:
|
webview_flutter_platform_interface:
|
||||||
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.36+230 # When changing this, update the tag in main() accordingly
|
version: 0.14.37+231 # 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'
|
||||||
@@ -67,6 +67,7 @@ dependencies:
|
|||||||
connectivity_plus: ^5.0.0
|
connectivity_plus: ^5.0.0
|
||||||
shared_storage: ^0.8.0
|
shared_storage: ^0.8.0
|
||||||
crypto: ^3.0.3
|
crypto: ^3.0.3
|
||||||
|
app_links: ^3.5.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user