mirror of
https://git.sr.ht/~thestr4ng3r/chiaki
synced 2025-08-19 21:13:12 -07:00
Export Settings on Android
This commit is contained in:
parent
3e2e12d002
commit
e3ea1e40b2
10 changed files with 316 additions and 4 deletions
|
@ -111,4 +111,6 @@ dependencies {
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
implementation "androidx.room:room-ktx:$room_version"
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
implementation "androidx.room:room-rxjava2:$room_version"
|
implementation "androidx.room:room-rxjava2:$room_version"
|
||||||
|
implementation "com.squareup.moshi:moshi:1.9.2"
|
||||||
|
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.9.2"
|
||||||
}
|
}
|
||||||
|
|
69
android/app/proguard-rules.pro
vendored
69
android/app/proguard-rules.pro
vendored
|
@ -21,4 +21,71 @@
|
||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
-dontobfuscate
|
-dontobfuscate
|
||||||
-keep class com.metallic.chiaki.** { *; }
|
-keep class com.metallic.chiaki.** { *; }
|
||||||
|
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
# Moshi
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
# JSR 305 annotations are for embedding nullability information.
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
|
-keepclasseswithmembers class * {
|
||||||
|
@com.squareup.moshi.* <methods>;
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep @com.squareup.moshi.JsonQualifier interface *
|
||||||
|
|
||||||
|
# Enum field names are used by the integrated EnumJsonAdapter.
|
||||||
|
# values() is synthesized by the Kotlin compiler and is used by EnumJsonAdapter indirectly
|
||||||
|
# Annotate enums with @JsonClass(generateAdapter = false) to use them with Moshi.
|
||||||
|
-keepclassmembers @com.squareup.moshi.JsonClass class * extends java.lang.Enum {
|
||||||
|
<fields>;
|
||||||
|
**[] values();
|
||||||
|
}
|
||||||
|
|
||||||
|
# The name of @JsonClass types is used to look up the generated adapter.
|
||||||
|
-keepnames @com.squareup.moshi.JsonClass class *
|
||||||
|
|
||||||
|
# Retain generated target class's synthetic defaults constructor and keep DefaultConstructorMarker's
|
||||||
|
# name. We will look this up reflectively to invoke the type's constructor.
|
||||||
|
#
|
||||||
|
# We can't _just_ keep the defaults constructor because Proguard/R8's spec doesn't allow wildcard
|
||||||
|
# matching preceding parameters.
|
||||||
|
-keepnames class kotlin.jvm.internal.DefaultConstructorMarker
|
||||||
|
-keepclassmembers @com.squareup.moshi.JsonClass @kotlin.Metadata class * {
|
||||||
|
synthetic <init>(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Retain generated JsonAdapters if annotated type is retained.
|
||||||
|
-if @com.squareup.moshi.JsonClass class *
|
||||||
|
-keep class <1>JsonAdapter {
|
||||||
|
<init>(...);
|
||||||
|
<fields>;
|
||||||
|
}
|
||||||
|
-if @com.squareup.moshi.JsonClass class **$*
|
||||||
|
-keep class <1>_<2>JsonAdapter {
|
||||||
|
<init>(...);
|
||||||
|
<fields>;
|
||||||
|
}
|
||||||
|
-if @com.squareup.moshi.JsonClass class **$*$*
|
||||||
|
-keep class <1>_<2>_<3>JsonAdapter {
|
||||||
|
<init>(...);
|
||||||
|
<fields>;
|
||||||
|
}
|
||||||
|
-if @com.squareup.moshi.JsonClass class **$*$*$*
|
||||||
|
-keep class <1>_<2>_<3>_<4>JsonAdapter {
|
||||||
|
<init>(...);
|
||||||
|
<fields>;
|
||||||
|
}
|
||||||
|
-if @com.squareup.moshi.JsonClass class **$*$*$*$*
|
||||||
|
-keep class <1>_<2>_<3>_<4>_<5>JsonAdapter {
|
||||||
|
<init>(...);
|
||||||
|
<fields>;
|
||||||
|
}
|
||||||
|
-if @com.squareup.moshi.JsonClass class **$*$*$*$*$*
|
||||||
|
-keep class <1>_<2>_<3>_<4>_<5>_<6>JsonAdapter {
|
||||||
|
<init>(...);
|
||||||
|
<fields>;
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
|
|
||||||
package com.metallic.chiaki.common
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
|
import com.squareup.moshi.FromJson
|
||||||
|
import com.squareup.moshi.JsonDataException
|
||||||
|
import com.squareup.moshi.ToJson
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
|
|
||||||
|
@ -38,6 +41,14 @@ class MacAddress(v: Long)
|
||||||
buf.getLong(0)
|
buf.getLong(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
constructor(string: String) : this(
|
||||||
|
(Regex("([0-9A-Fa-f]{2})[:-]{5}([0-9A-Fa-f]{2})").matchEntire(string)
|
||||||
|
?: throw IllegalArgumentException("Invalid MAC Address String"))
|
||||||
|
.groupValues
|
||||||
|
.subList(1, 7)
|
||||||
|
.map { it.toByte() }
|
||||||
|
.toByteArray())
|
||||||
|
|
||||||
val value: Long = v and 0xffffffffffff
|
val value: Long = v and 0xffffffffffff
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
|
@ -56,4 +67,10 @@ class MacAddress(v: Long)
|
||||||
(value shr 0x20) and 0xff,
|
(value shr 0x20) and 0xff,
|
||||||
(value shr 0x28) and 0xff
|
(value shr 0x28) and 0xff
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class MacAddressJsonAdapter
|
||||||
|
{
|
||||||
|
@ToJson fun toJson(macAddress: MacAddress) = macAddress.toString()
|
||||||
|
@FromJson fun fromJson(string: String) = try { MacAddress(string) } catch(e: IllegalArgumentException) { throw JsonDataException(e.message) }
|
||||||
}
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Chiaki.
|
||||||
|
*
|
||||||
|
* Chiaki is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Chiaki is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Base64
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import com.metallic.chiaki.R
|
||||||
|
import com.squareup.moshi.*
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.Disposable
|
||||||
|
import io.reactivex.rxkotlin.Singles
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import okio.Buffer
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class SerializedRegisteredHost(
|
||||||
|
@Json(name = "ap_ssid") val apSsid: String?,
|
||||||
|
@Json(name = "ap_bssid") val apBssid: String?,
|
||||||
|
@Json(name = "ap_key") val apKey: String?,
|
||||||
|
@Json(name = "ap_name") val apName: String?,
|
||||||
|
@Json(name = "ps4_mac") val ps4Mac: MacAddress,
|
||||||
|
@Json(name = "ps4_nickname") val ps4Nickname: String?,
|
||||||
|
@Json(name = "rp_regist_key") val rpRegistKey: ByteArray,
|
||||||
|
@Json(name = "rp_key_type") val rpKeyType: Int,
|
||||||
|
@Json(name = "rp_key") val rpKey: ByteArray
|
||||||
|
){
|
||||||
|
constructor(registeredHost: RegisteredHost) : this(
|
||||||
|
registeredHost.apSsid,
|
||||||
|
registeredHost.apBssid,
|
||||||
|
registeredHost.apKey,
|
||||||
|
registeredHost.apName,
|
||||||
|
registeredHost.ps4Mac,
|
||||||
|
registeredHost.ps4Nickname,
|
||||||
|
registeredHost.rpRegistKey,
|
||||||
|
registeredHost.rpKeyType,
|
||||||
|
registeredHost.rpKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class SerializedManualHost(
|
||||||
|
@Json(name = "host") val host: String,
|
||||||
|
@Json(name = "ps4_mac") val ps4Mac: MacAddress?
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class SerializedSettings(
|
||||||
|
@Json(name = "registered_hosts") val registeredHosts: List<SerializedRegisteredHost>,
|
||||||
|
@Json(name = "manual_hosts") val manualHosts: List<SerializedManualHost>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
companion object
|
||||||
|
{
|
||||||
|
fun fromDatabase(db: AppDatabase) = Singles.zip(
|
||||||
|
db.registeredHostDao().getAll().firstOrError(),
|
||||||
|
db.manualHostDao().getAll().firstOrError()
|
||||||
|
) { registeredHosts, manualHosts ->
|
||||||
|
SerializedSettings(
|
||||||
|
registeredHosts.map { SerializedRegisteredHost(it) },
|
||||||
|
manualHosts.map { manualHost ->
|
||||||
|
SerializedManualHost(
|
||||||
|
manualHost.host,
|
||||||
|
manualHost.registeredHost?.let { registeredHostId ->
|
||||||
|
registeredHosts.firstOrNull { it.id == registeredHostId }
|
||||||
|
}?.ps4Mac
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ByteArrayJsonAdapter
|
||||||
|
{
|
||||||
|
@ToJson fun toJson(byteArray: ByteArray) = Base64.encodeToString(byteArray, Base64.NO_WRAP)
|
||||||
|
@FromJson fun fromJson(string: String) = Base64.decode(string, Base64.DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun moshi() =
|
||||||
|
Moshi.Builder()
|
||||||
|
.add(MacAddressJsonAdapter())
|
||||||
|
.add(ByteArrayJsonAdapter())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private const val KEY_FORMAT = "format"
|
||||||
|
private const val FORMAT = "chiaki-settings"
|
||||||
|
private const val KEY_VERSION = "version"
|
||||||
|
private const val VERSION = "1"
|
||||||
|
private const val KEY_SETTINGS = "settings"
|
||||||
|
|
||||||
|
fun exportAllSettings(db: AppDatabase) = SerializedSettings.fromDatabase(db)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.map {
|
||||||
|
val buffer = Buffer()
|
||||||
|
val writer = JsonWriter.of(buffer)
|
||||||
|
val adapter = moshi()
|
||||||
|
.adapter(SerializedSettings::class.java)
|
||||||
|
.serializeNulls()
|
||||||
|
writer.indent = " "
|
||||||
|
writer.
|
||||||
|
beginObject()
|
||||||
|
.name(KEY_FORMAT).value(FORMAT)
|
||||||
|
.name(KEY_VERSION).value(VERSION)
|
||||||
|
writer.name(KEY_SETTINGS)
|
||||||
|
adapter.toJson(writer, it)
|
||||||
|
writer.endObject()
|
||||||
|
buffer.readUtf8()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportAndShareAllSettings(db: AppDatabase, activity: Activity): Disposable
|
||||||
|
{
|
||||||
|
val dir = File(activity.cacheDir, "export_settings")
|
||||||
|
dir.mkdirs()
|
||||||
|
val file = File(dir, "chiaki-settings.json")
|
||||||
|
return exportAllSettings(db)
|
||||||
|
.map {
|
||||||
|
file.writeText(it, Charsets.UTF_8)
|
||||||
|
file
|
||||||
|
}
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { it: File ->
|
||||||
|
val uri = FileProvider.getUriForFile(activity, fileProviderAuthority, file)
|
||||||
|
Intent(Intent.ACTION_SEND).also {
|
||||||
|
it.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
it.type = "application/json"
|
||||||
|
it.putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
it.clipData = ClipData.newRawUri("", uri)
|
||||||
|
activity.startActivity(Intent.createChooser(it, activity.getString(R.string.action_share_log)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,17 +17,24 @@
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.Intent
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import androidx.preference.*
|
import androidx.preference.*
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
import com.metallic.chiaki.common.Preferences
|
import com.metallic.chiaki.common.*
|
||||||
import com.metallic.chiaki.common.ext.toLiveData
|
import com.metallic.chiaki.common.ext.toLiveData
|
||||||
import com.metallic.chiaki.common.ext.viewModelFactory
|
import com.metallic.chiaki.common.ext.viewModelFactory
|
||||||
import com.metallic.chiaki.common.getDatabase
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.rxkotlin.addTo
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class DataStore(val preferences: Preferences): PreferenceDataStore()
|
class DataStore(val preferences: Preferences): PreferenceDataStore()
|
||||||
{
|
{
|
||||||
|
@ -76,11 +83,15 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
|
||||||
|
|
||||||
class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
|
class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
|
||||||
{
|
{
|
||||||
|
private lateinit var viewModel: SettingsViewModel
|
||||||
|
|
||||||
|
private var disposable = CompositeDisposable()
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?)
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?)
|
||||||
{
|
{
|
||||||
val context = context ?: return
|
val context = context ?: return
|
||||||
|
|
||||||
val viewModel = ViewModelProviders
|
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)
|
||||||
|
|
||||||
|
@ -118,7 +129,28 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
|
||||||
viewModel.registeredHostsCount.observe(this, Observer {
|
viewModel.registeredHostsCount.observe(this, Observer {
|
||||||
registeredHostsPreference?.summary = getString(R.string.preferences_registered_hosts_summary, it)
|
registeredHostsPreference?.summary = getString(R.string.preferences_registered_hosts_summary, it)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
preferenceScreen.findPreference<Preference>(getString(R.string.preferences_export_settings_key))?.setOnPreferenceClickListener { exportSettings(); true }
|
||||||
|
preferenceScreen.findPreference<Preference>(getString(R.string.preferences_import_settings_key))?.setOnPreferenceClickListener { importSettings(); true }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy()
|
||||||
|
{
|
||||||
|
super.onDestroy()
|
||||||
|
disposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTitle(resources: Resources): String = resources.getString(R.string.title_settings)
|
override fun getTitle(resources: Resources): String = resources.getString(R.string.title_settings)
|
||||||
|
|
||||||
|
private fun exportSettings()
|
||||||
|
{
|
||||||
|
val activity = activity ?: return
|
||||||
|
disposable.clear()
|
||||||
|
exportAndShareAllSettings(viewModel.database, activity).addTo(disposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun importSettings()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
9
android/app/src/main/res/drawable/ic_export.xml
Normal file
9
android/app/src/main/res/drawable/ic_export.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
|
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
|
||||||
|
</vector>
|
9
android/app/src/main/res/drawable/ic_import.xml
Normal file
9
android/app/src/main/res/drawable/ic_import.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
|
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
|
||||||
|
</vector>
|
|
@ -40,6 +40,7 @@
|
||||||
<string name="regist_psn_account_id_invalid">Please enter a valid 8-byte Account ID in Base64</string>
|
<string name="regist_psn_account_id_invalid">Please enter a valid 8-byte Account ID in Base64</string>
|
||||||
<string name="regist_pin_invalid">Please enter a valid %d-digit PIN</string>
|
<string name="regist_pin_invalid">Please enter a valid %d-digit PIN</string>
|
||||||
<string name="action_share_log">Share Log</string>
|
<string name="action_share_log">Share Log</string>
|
||||||
|
<string name="action_export_settings">Export Settings</string>
|
||||||
<string name="regist_info_success">Regist successful.</string>
|
<string name="regist_info_success">Regist successful.</string>
|
||||||
<string name="regist_info_failed">Regist failed.</string>
|
<string name="regist_info_failed">Regist failed.</string>
|
||||||
<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>
|
||||||
|
@ -51,9 +52,14 @@
|
||||||
<string name="add_manual_regist_on_connect">Register on first Connection</string>
|
<string name="add_manual_regist_on_connect">Register on first Connection</string>
|
||||||
<string name="preferences_category_title_general">General</string>
|
<string name="preferences_category_title_general">General</string>
|
||||||
<string name="preferences_category_title_stream">Stream</string>
|
<string name="preferences_category_title_stream">Stream</string>
|
||||||
|
<string name="preferences_category_title_export">Export</string>
|
||||||
<string name="preferences_registered_hosts_title">Registered Consoles</string>
|
<string name="preferences_registered_hosts_title">Registered Consoles</string>
|
||||||
<string name="preferences_logs_title">Session Logs</string>
|
<string name="preferences_logs_title">Session Logs</string>
|
||||||
<string name="preferences_logs_summary">Collected log files from previous sessions for debugging</string>
|
<string name="preferences_logs_summary">Collected log files from previous sessions for debugging</string>
|
||||||
|
<string name="preferences_export_settings_title">Export Settings</string>
|
||||||
|
<string name="preferences_export_settings_summary">Warning: These resulting file can contain your secret Remote Play keys! Do not share them.</string>
|
||||||
|
<string name="preferences_import_settings_title">Import Settings</string>
|
||||||
|
<string name="preferences_import_settings_summary">Import Settings from JSON</string>
|
||||||
<string name="preferences_registered_hosts_summary">Currently registered: %d</string>
|
<string name="preferences_registered_hosts_summary">Currently registered: %d</string>
|
||||||
<string name="preferences_resolution_title">Resolution</string>
|
<string name="preferences_resolution_title">Resolution</string>
|
||||||
<string name="preferences_fps_title">FPS</string>
|
<string name="preferences_fps_title">FPS</string>
|
||||||
|
@ -83,6 +89,8 @@
|
||||||
<string name="preferences_discovery_enabled_key">discovery_enabled</string>
|
<string name="preferences_discovery_enabled_key">discovery_enabled</string>
|
||||||
<string name="preferences_on_screen_controls_enabled_key">on_screen_controls_enabled</string>
|
<string name="preferences_on_screen_controls_enabled_key">on_screen_controls_enabled</string>
|
||||||
<string name="preferences_log_verbose_key">log_verbose</string>
|
<string name="preferences_log_verbose_key">log_verbose</string>
|
||||||
|
<string name="preferences_import_settings_key">import_settings</string>
|
||||||
|
<string name="preferences_export_settings_key">export_settings</string>
|
||||||
<string name="preferences_swap_cross_moon_key">swap_cross_moon</string>
|
<string name="preferences_swap_cross_moon_key">swap_cross_moon</string>
|
||||||
<string name="preferences_resolution_key">stream_resolution</string>
|
<string name="preferences_resolution_key">stream_resolution</string>
|
||||||
<string name="preferences_fps_key">stream_fps</string>
|
<string name="preferences_fps_key">stream_fps</string>
|
||||||
|
|
|
@ -3,4 +3,7 @@
|
||||||
<files-path
|
<files-path
|
||||||
name="session_logs"
|
name="session_logs"
|
||||||
path="session_logs/" /> <!-- must be in sync with LogManager -->
|
path="session_logs/" /> <!-- must be in sync with LogManager -->
|
||||||
|
<cache-path
|
||||||
|
name="export_settings"
|
||||||
|
path="export_settings/"/> <!-- must be in sync with SerializedSettings.kt -->
|
||||||
</paths>
|
</paths>
|
|
@ -53,4 +53,20 @@
|
||||||
app:title="@string/preferences_bitrate_title"
|
app:title="@string/preferences_bitrate_title"
|
||||||
app:icon="@drawable/ic_bitrate"/>
|
app:icon="@drawable/ic_bitrate"/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
app:key="category_export"
|
||||||
|
app:title="@string/preferences_category_title_export">
|
||||||
|
<Preference
|
||||||
|
app:key="@string/preferences_export_settings_key"
|
||||||
|
app:summary="@string/preferences_export_settings_summary"
|
||||||
|
app:title="@string/preferences_export_settings_title"
|
||||||
|
app:icon="@drawable/ic_export"/>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:key="@string/preferences_import_settings_key"
|
||||||
|
app:summary="@string/preferences_import_settings_summary"
|
||||||
|
app:title="@string/preferences_import_settings_title"
|
||||||
|
app:icon="@drawable/ic_import"/>
|
||||||
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
Loading…
Add table
Add a link
Reference in a new issue