From 5ddf9ef3732391a68bc36f8a227e266c8544c765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sat, 30 Nov 2019 20:25:05 +0100 Subject: [PATCH] Finish Settings Import on Android --- .../com/metallic/chiaki/common/AppDatabase.kt | 1 + .../chiaki/common/SerializedSettings.kt | 105 +++++++++++++++++- .../chiaki/settings/SettingsFragment.kt | 7 +- android/app/src/main/res/values/strings.xml | 6 + android/build.gradle | 2 +- 5 files changed, 111 insertions(+), 10 deletions(-) diff --git a/android/app/src/main/java/com/metallic/chiaki/common/AppDatabase.kt b/android/app/src/main/java/com/metallic/chiaki/common/AppDatabase.kt index 165574b..64c1288 100644 --- a/android/app/src/main/java/com/metallic/chiaki/common/AppDatabase.kt +++ b/android/app/src/main/java/com/metallic/chiaki/common/AppDatabase.kt @@ -28,6 +28,7 @@ abstract class AppDatabase: RoomDatabase() { abstract fun registeredHostDao(): RegisteredHostDao abstract fun manualHostDao(): ManualHostDao + abstract fun importDao(): ImportDao } private var database: AppDatabase? = null diff --git a/android/app/src/main/java/com/metallic/chiaki/common/SerializedSettings.kt b/android/app/src/main/java/com/metallic/chiaki/common/SerializedSettings.kt index e7d9098..174e620 100644 --- a/android/app/src/main/java/com/metallic/chiaki/common/SerializedSettings.kt +++ b/android/app/src/main/java/com/metallic/chiaki/common/SerializedSettings.kt @@ -24,11 +24,18 @@ import android.net.Uri import android.util.Base64 import android.util.Log import androidx.core.content.FileProvider +import androidx.room.* +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.metallic.chiaki.R import com.squareup.moshi.* +import io.reactivex.Completable +import io.reactivex.Flowable +import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable import io.reactivex.rxkotlin.Singles +import io.reactivex.rxkotlin.addTo import io.reactivex.schedulers.Schedulers import okio.Buffer import okio.Okio @@ -155,8 +162,17 @@ fun exportAndShareAllSettings(activity: Activity): Disposable } } -fun importSettingsFromUri(activity: Activity, uri: Uri) +fun importSettingsFromUri(activity: Activity, uri: Uri, disposable: CompositeDisposable) { + fun loadFail(msg: String) + { + MaterialAlertDialogBuilder(activity) + .setMessage(activity.getString(R.string.alert_message_import_failed, msg)) + .setPositiveButton(R.string.action_import_failed_ack) { _, _ -> } + .create() + .show() + } + try { val inputStream = activity.contentResolver.openInputStream(uri) ?: throw IOException() @@ -187,19 +203,96 @@ fun importSettingsFromUri(activity: Activity, uri: Uri) if(version != VERSION) // Add migrations here when necessary throw IOException("Value of version is invalid") - val settings = adapter.fromJsonValue(settingsValue) + val settings = adapter.fromJsonValue(settingsValue) ?: throw JsonDataException("Failed to parse Settings JSON") Log.i("SerializedSettings", "would import: $settings") - // TODO: show dialog and import + + MaterialAlertDialogBuilder(activity) + .setMessage(activity.getString(R.string.alert_message_import, + settings.registeredHosts.let { + if(it.isEmpty()) + "-" + else + it.joinToString(separator = "") { host -> "\n - ${host.ps4Nickname ?: "?"} / ${host.ps4Mac}" } + }, + settings.manualHosts.let { + if(it.isEmpty()) + "-" + else + it.joinToString(separator = "") { host -> "\n - ${host.host} / ${host.ps4Mac ?: "unregistered"}" } + } + )) + .setTitle(R.string.alert_title_import) + .setNegativeButton(R.string.action_import_cancel) { _, _ -> } + .setPositiveButton(R.string.action_import_import) { _, _ -> + getDatabase(activity).importDao() + .importCompletable(settings) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe() + .addTo(disposable) + } + .create() + .show() } catch(e: IOException) { - // TODO e.printStackTrace() + loadFail(e.message ?: "") } catch(e: JsonDataException) { - // TODO e.printStackTrace() + loadFail(e.message ?: "") } +} -} \ No newline at end of file +@Dao +abstract class ImportDao +{ + @Insert + abstract fun insertRegisteredHosts(hosts: List) + + @Insert + abstract fun insertManualHosts(hosts: List) + + class IdWithMac(val id: Long, val mac: MacAddress) + + @Query("SELECT id, ps4_mac AS mac FROM registered_host WHERE ps4_mac IN (:macs)") + abstract fun registeredHostsByMac(macs: List): List + + @Transaction + fun import(settings: SerializedSettings) + { + insertRegisteredHosts( + settings.registeredHosts.map { + RegisteredHost( + apSsid = it.apSsid, + apBssid = it.apBssid, + apKey = it.apKey, + apName = it.apName, + ps4Mac = it.ps4Mac, + ps4Nickname = it.ps4Nickname, + rpRegistKey = it.rpRegistKey, + rpKeyType = it.rpKeyType, + rpKey = it.rpKey + ) + }) + + val macs = settings.manualHosts.mapNotNull { it.ps4Mac } + val idMacs = + if(macs.isNotEmpty()) + registeredHostsByMac(macs) + else + listOf() + + insertManualHosts( + settings.manualHosts.map { + ManualHost( + host = it.host, + registeredHost = idMacs.firstOrNull { regHost -> regHost.mac == it.ps4Mac }?.id + ) + }) + } +} + +private fun ImportDao.importCompletable(settings: SerializedSettings) = Completable.fromCallable { import(settings) } diff --git a/android/app/src/main/java/com/metallic/chiaki/settings/SettingsFragment.kt b/android/app/src/main/java/com/metallic/chiaki/settings/SettingsFragment.kt index 336c296..ee2cf57 100644 --- a/android/app/src/main/java/com/metallic/chiaki/settings/SettingsFragment.kt +++ b/android/app/src/main/java/com/metallic/chiaki/settings/SettingsFragment.kt @@ -90,6 +90,7 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment } private var disposable = CompositeDisposable() + private var exportDisposable = CompositeDisposable().also { it.addTo(disposable) } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { @@ -149,8 +150,8 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment private fun exportSettings() { val activity = activity ?: return - disposable.clear() - exportAndShareAllSettings(activity).addTo(disposable) + exportDisposable.clear() + exportAndShareAllSettings(activity).addTo(exportDisposable) } private fun importSettings() @@ -168,7 +169,7 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment { val activity = activity ?: return data?.data?.also { - importSettingsFromUri(activity, it) + importSettingsFromUri(activity, it, disposable) } } } diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 51d0617..38739a8 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -46,6 +46,12 @@ The console with MAC %s has already been registered. Should the previous record be overwritten? Overwrite Cancel + Import + Cancel + Import + Registered Hosts: %s\n\nManual Hosts: %s + Failed to import settings from file:\n%s + Dismiss Manual Console Entry Save Registered Console diff --git a/android/build.gradle b/android/build.gradle index f4ed563..f672437 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.60' + ext.kotlin_version = '1.3.61' repositories { google() jcenter()