Prepare Importing Settings on Android

This commit is contained in:
Florian Märkl 2019-11-25 20:39:37 +01:00
commit 2aa297d203
No known key found for this signature in database
GPG key ID: 125BC8A5A6A1E857
3 changed files with 85 additions and 10 deletions

View file

@ -42,11 +42,11 @@ class MacAddress(v: Long)
}) })
constructor(string: String) : this( constructor(string: String) : this(
(Regex("([0-9A-Fa-f]{2})[:-]{5}([0-9A-Fa-f]{2})").matchEntire(string) (Regex("([0-9A-Fa-f]{2})[:-]([0-9A-Fa-f]{2})[:-]([0-9A-Fa-f]{2})[:-]([0-9A-Fa-f]{2})[:-]([0-9A-Fa-f]{2})[:-]([0-9A-Fa-f]{2})").matchEntire(string)
?: throw IllegalArgumentException("Invalid MAC Address String")) ?: throw IllegalArgumentException("Invalid MAC Address String"))
.groupValues .groupValues
.subList(1, 7) .subList(1, 7)
.map { it.toByte() } .map { it.toUByte(16).toByte() }
.toByteArray()) .toByteArray())
val value: Long = v and 0xffffffffffff val value: Long = v and 0xffffffffffff

View file

@ -20,7 +20,9 @@ package com.metallic.chiaki.common
import android.app.Activity import android.app.Activity
import android.content.ClipData import android.content.ClipData
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.util.Base64 import android.util.Base64
import android.util.Log
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import com.metallic.chiaki.R import com.metallic.chiaki.R
import com.squareup.moshi.* import com.squareup.moshi.*
@ -29,7 +31,9 @@ import io.reactivex.disposables.Disposable
import io.reactivex.rxkotlin.Singles import io.reactivex.rxkotlin.Singles
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import okio.Buffer import okio.Buffer
import okio.Okio
import java.io.File import java.io.File
import java.io.IOException
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
class SerializedRegisteredHost( class SerializedRegisteredHost(
@ -100,10 +104,14 @@ private fun moshi() =
.add(ByteArrayJsonAdapter()) .add(ByteArrayJsonAdapter())
.build() .build()
private fun Moshi.serializedSettingsAdapter() =
adapter(SerializedSettings::class.java)
.serializeNulls()
private const val KEY_FORMAT = "format" private const val KEY_FORMAT = "format"
private const val FORMAT = "chiaki-settings" private const val FORMAT = "chiaki-settings"
private const val KEY_VERSION = "version" private const val KEY_VERSION = "version"
private const val VERSION = "1" private const val VERSION = 1
private const val KEY_SETTINGS = "settings" private const val KEY_SETTINGS = "settings"
fun exportAllSettings(db: AppDatabase) = SerializedSettings.fromDatabase(db) fun exportAllSettings(db: AppDatabase) = SerializedSettings.fromDatabase(db)
@ -111,9 +119,7 @@ fun exportAllSettings(db: AppDatabase) = SerializedSettings.fromDatabase(db)
.map { .map {
val buffer = Buffer() val buffer = Buffer()
val writer = JsonWriter.of(buffer) val writer = JsonWriter.of(buffer)
val adapter = moshi() val adapter = moshi().serializedSettingsAdapter()
.adapter(SerializedSettings::class.java)
.serializeNulls()
writer.indent = " " writer.indent = " "
writer. writer.
beginObject() beginObject()
@ -125,8 +131,9 @@ fun exportAllSettings(db: AppDatabase) = SerializedSettings.fromDatabase(db)
buffer.readUtf8() buffer.readUtf8()
} }
fun exportAndShareAllSettings(db: AppDatabase, activity: Activity): Disposable fun exportAndShareAllSettings(activity: Activity): Disposable
{ {
val db = getDatabase(activity)
val dir = File(activity.cacheDir, "export_settings") val dir = File(activity.cacheDir, "export_settings")
dir.mkdirs() dir.mkdirs()
val file = File(dir, "chiaki-settings.json") val file = File(dir, "chiaki-settings.json")
@ -147,3 +154,52 @@ fun exportAndShareAllSettings(db: AppDatabase, activity: Activity): Disposable
} }
} }
} }
fun importSettingsFromUri(activity: Activity, uri: Uri)
{
try
{
val inputStream = activity.contentResolver.openInputStream(uri) ?: throw IOException()
val buffer = Okio.buffer(Okio.source(inputStream))
val reader = JsonReader.of(buffer)
val adapter = moshi().serializedSettingsAdapter()
var format: String? = null
var version: Int? = null
var settingsValue: Any? = null
reader.beginObject()
while(reader.hasNext())
{
when(reader.nextName())
{
KEY_FORMAT -> format = reader.nextString()
KEY_VERSION -> version = reader.nextInt()
KEY_SETTINGS -> settingsValue = reader.readJsonValue()
}
}
reader.endObject()
if(format == null || version == null || settingsValue == null)
throw IOException("Missing format, version or settings from JSON")
if(format != FORMAT)
throw IOException("Value of format is invalid")
if(version != VERSION) // Add migrations here when necessary
throw IOException("Value of version is invalid")
val settings = adapter.fromJsonValue(settingsValue)
Log.i("SerializedSettings", "would import: $settings")
// TODO: show dialog and import
}
catch(e: IOException)
{
// TODO
e.printStackTrace()
}
catch(e: JsonDataException)
{
// TODO
e.printStackTrace()
}
}

View file

@ -17,6 +17,7 @@
package com.metallic.chiaki.settings package com.metallic.chiaki.settings
import android.app.Activity
import android.content.ClipData import android.content.ClipData
import android.content.Intent import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
@ -83,7 +84,10 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
class SettingsFragment: PreferenceFragmentCompat(), TitleFragment class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
{ {
private lateinit var viewModel: SettingsViewModel companion object
{
private const val PICK_SETTINGS_JSON_REQUEST = 1
}
private var disposable = CompositeDisposable() private var disposable = CompositeDisposable()
@ -91,7 +95,7 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
{ {
val context = context ?: return val context = context ?: return
viewModel = ViewModelProviders val viewModel = ViewModelProviders
.of(this, viewModelFactory { SettingsViewModel(getDatabase(context), Preferences(context)) }) .of(this, viewModelFactory { SettingsViewModel(getDatabase(context), Preferences(context)) })
.get(SettingsViewModel::class.java) .get(SettingsViewModel::class.java)
@ -146,11 +150,26 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
{ {
val activity = activity ?: return val activity = activity ?: return
disposable.clear() disposable.clear()
exportAndShareAllSettings(viewModel.database, activity).addTo(disposable) exportAndShareAllSettings(activity).addTo(disposable)
} }
private fun importSettings() private fun importSettings()
{ {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/json"
}
startActivityForResult(intent, PICK_SETTINGS_JSON_REQUEST)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
{
if(requestCode == PICK_SETTINGS_JSON_REQUEST && resultCode == Activity.RESULT_OK)
{
val activity = activity ?: return
data?.data?.also {
importSettingsFromUri(activity, it)
}
}
} }
} }