Finish Settings Import on Android

This commit is contained in:
Florian Märkl 2019-11-30 20:25:05 +01:00
commit 5ddf9ef373
No known key found for this signature in database
GPG key ID: 125BC8A5A6A1E857
5 changed files with 111 additions and 10 deletions

View file

@ -28,6 +28,7 @@ abstract class AppDatabase: RoomDatabase()
{ {
abstract fun registeredHostDao(): RegisteredHostDao abstract fun registeredHostDao(): RegisteredHostDao
abstract fun manualHostDao(): ManualHostDao abstract fun manualHostDao(): ManualHostDao
abstract fun importDao(): ImportDao
} }
private var database: AppDatabase? = null private var database: AppDatabase? = null

View file

@ -24,11 +24,18 @@ import android.net.Uri
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.room.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.metallic.chiaki.R import com.metallic.chiaki.R
import com.squareup.moshi.* 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.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.rxkotlin.Singles import io.reactivex.rxkotlin.Singles
import io.reactivex.rxkotlin.addTo
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import okio.Buffer import okio.Buffer
import okio.Okio 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 try
{ {
val inputStream = activity.contentResolver.openInputStream(uri) ?: throw IOException() 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 if(version != VERSION) // Add migrations here when necessary
throw IOException("Value of version is invalid") 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") 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) catch(e: IOException)
{ {
// TODO
e.printStackTrace() e.printStackTrace()
loadFail(e.message ?: "")
} }
catch(e: JsonDataException) catch(e: JsonDataException)
{ {
// TODO
e.printStackTrace() e.printStackTrace()
loadFail(e.message ?: "")
} }
}
} @Dao
abstract class ImportDao
{
@Insert
abstract fun insertRegisteredHosts(hosts: List<RegisteredHost>)
@Insert
abstract fun insertManualHosts(hosts: List<ManualHost>)
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<MacAddress>): List<IdWithMac>
@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) }

View file

@ -90,6 +90,7 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
} }
private var disposable = CompositeDisposable() private var disposable = CompositeDisposable()
private var exportDisposable = CompositeDisposable().also { it.addTo(disposable) }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?)
{ {
@ -149,8 +150,8 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
private fun exportSettings() private fun exportSettings()
{ {
val activity = activity ?: return val activity = activity ?: return
disposable.clear() exportDisposable.clear()
exportAndShareAllSettings(activity).addTo(disposable) exportAndShareAllSettings(activity).addTo(exportDisposable)
} }
private fun importSettings() private fun importSettings()
@ -168,7 +169,7 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
{ {
val activity = activity ?: return val activity = activity ?: return
data?.data?.also { data?.data?.also {
importSettingsFromUri(activity, it) importSettingsFromUri(activity, it, disposable)
} }
} }
} }

View file

@ -46,6 +46,12 @@
<string name="alert_regist_duplicate">The console with MAC %s has already been registered. Should the previous record be overwritten?</string> <string name="alert_regist_duplicate">The console with MAC %s has already been registered. Should the previous record be overwritten?</string>
<string name="action_regist_overwrite">Overwrite</string> <string name="action_regist_overwrite">Overwrite</string>
<string name="action_regist_discard">Cancel</string> <string name="action_regist_discard">Cancel</string>
<string name="action_import_import">Import</string>
<string name="action_import_cancel">Cancel</string>
<string name="alert_title_import">Import</string>
<string name="alert_message_import">Registered Hosts: %s\n\nManual Hosts: %s</string>
<string name="alert_message_import_failed">Failed to import settings from file:\n%s</string>
<string name="action_import_failed_ack">Dismiss</string>
<string name="title_edit_manual">Manual Console Entry</string> <string name="title_edit_manual">Manual Console Entry</string>
<string name="action_add_manual_save">Save</string> <string name="action_add_manual_save">Save</string>
<string name="hint_add_manual_regist_host">Registered Console</string> <string name="hint_add_manual_regist_host">Registered Console</string>

View file

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.60' ext.kotlin_version = '1.3.61'
repositories { repositories {
google() google()
jcenter() jcenter()