Skip to content

Commit 37a2ee8

Browse files
committed
feature: backup params
1 parent 066dcb2 commit 37a2ee8

33 files changed

Lines changed: 740 additions & 174 deletions

app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
<activity
4141
android:name=".ui.params.user.ManageOnStartUpParamsActivity"
4242
android:label="@string/manage_parameters" />
43+
<activity
44+
android:name=".ui.export.ExportOptionsActivity"
45+
android:label="@string/export_options"
46+
android:theme="@style/AppTheme.NoActionBar" />
4347
<activity
4448
android:name=".ui.SplashActivity"
4549
android:theme="@style/AppTheme.NoActionBar">
@@ -103,7 +107,7 @@
103107
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
104108

105109
<intent-filter>
106-
<action android:name="android.service.quicksettings.action.QS_TILE"/>
110+
<action android:name="android.service.quicksettings.action.QS_TILE" />
107111
</intent-filter>
108112
</service>
109113
<service
@@ -113,7 +117,7 @@
113117
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
114118

115119
<intent-filter>
116-
<action android:name="android.service.quicksettings.action.QS_TILE"/>
120+
<action android:name="android.service.quicksettings.action.QS_TILE" />
117121
</intent-filter>
118122
</service>
119123
</application>

app/src/main/kotlin/com/androidvip/sysctlgui/data/models/HomeItem.kt renamed to app/src/main/kotlin/com/androidvip/sysctlgui/data/models/SettingsItem.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.androidvip.sysctlgui.data.models
33
import androidx.annotation.DrawableRes
44
import androidx.annotation.StringRes
55

6-
data class HomeItem(
6+
data class SettingsItem(
77
@StringRes val titleRes: Int,
88
@StringRes val descriptionRes: Int,
99
@DrawableRes val iconRes: Int

app/src/main/kotlin/com/androidvip/sysctlgui/di/PresentationModule.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.androidvip.sysctlgui.di
22

3+
import com.androidvip.sysctlgui.ui.export.ExportOptionsViewModel
34
import com.androidvip.sysctlgui.ui.main.MainViewModel
45
import com.androidvip.sysctlgui.ui.params.browse.BrowseParamsViewModel
56
import com.androidvip.sysctlgui.ui.params.list.ListParamsViewModel
@@ -15,6 +16,7 @@ internal val presentationModules = module {
1516
viewModel { ListParamsViewModel(get()) }
1617
viewModel { UserParamsViewModel(get(), get(), get()) }
1718
viewModel { MainViewModel() }
19+
viewModel { ExportOptionsViewModel(get(), get(), get()) }
1820

1921
single { FavoriteWidgetParamUpdater(androidContext()).getListener() }
2022
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.androidvip.sysctlgui.helpers
2+
3+
import com.androidvip.sysctlgui.data.models.SettingsItem
4+
5+
interface OnSettingsItemClickedListener {
6+
fun onSettingsItemClicked(item: SettingsItem, position: Int)
7+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.androidvip.sysctlgui.helpers
2+
3+
import androidx.recyclerview.widget.DiffUtil
4+
import com.androidvip.sysctlgui.data.models.SettingsItem
5+
6+
internal object SettingsItemDiffCallback : DiffUtil.ItemCallback<SettingsItem>() {
7+
override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
8+
return oldItem.titleRes == newItem.titleRes
9+
}
10+
11+
override fun areContentsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
12+
return oldItem == newItem
13+
}
14+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package com.androidvip.sysctlgui.ui.export
2+
3+
import android.app.Activity
4+
import android.content.Intent
5+
import android.os.Bundle
6+
import android.view.MenuItem
7+
import android.view.View
8+
import androidx.annotation.StringRes
9+
import androidx.appcompat.app.AppCompatActivity
10+
import com.androidvip.sysctlgui.R
11+
import com.androidvip.sysctlgui.data.models.SettingsItem
12+
import com.androidvip.sysctlgui.databinding.ActivityExportOptionsBinding
13+
import com.androidvip.sysctlgui.helpers.OnSettingsItemClickedListener
14+
import com.androidvip.sysctlgui.toast
15+
import com.google.android.material.dialog.MaterialAlertDialogBuilder
16+
import org.koin.androidx.viewmodel.ext.android.viewModel
17+
18+
class ExportOptionsActivity : AppCompatActivity(), OnSettingsItemClickedListener {
19+
private lateinit var binding: ActivityExportOptionsBinding
20+
private val viewModel: ExportOptionsViewModel by viewModel()
21+
22+
override fun onCreate(savedInstanceState: Bundle?) {
23+
super.onCreate(savedInstanceState)
24+
binding = ActivityExportOptionsBinding.inflate(layoutInflater)
25+
setContentView(binding.root)
26+
setSupportActionBar(binding.toolbar)
27+
supportActionBar?.setDisplayHomeAsUpEnabled(true)
28+
29+
val adapter = ExportOptionsItemAdapter(this)
30+
binding.recyclerView.adapter = adapter
31+
adapter.submitList(viewModel.getBackOptionItems())
32+
33+
observeUi()
34+
}
35+
36+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
37+
when (item.itemId) {
38+
android.R.id.home -> finish()
39+
}
40+
41+
return true
42+
}
43+
44+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
45+
if (resultCode != Activity.RESULT_OK) return toast(R.string.error)
46+
47+
when (requestCode) {
48+
RC_IMPORT_USER_PARAMS,
49+
RC_RESTORE_PARAMS -> {
50+
val uri = data?.data ?: return toast(R.string.import_error)
51+
val extension = uri.lastPathSegment.orEmpty()
52+
val stream = contentResolver.openInputStream(uri)
53+
?: return toast(R.string.import_error)
54+
viewModel.importParams(stream, extension)
55+
}
56+
57+
RC_EXPORT_USER_PARAMS -> {
58+
val uri = data?.data ?: return toast(R.string.export_error)
59+
viewModel.exportParams(uri, this, false)
60+
}
61+
62+
RC_BACKUP_PARAMS -> {
63+
val uri = data?.data ?: return toast(R.string.export_error)
64+
viewModel.exportParams(uri, this, true)
65+
}
66+
}
67+
super.onActivityResult(requestCode, resultCode, data)
68+
}
69+
70+
override fun onSettingsItemClicked(item: SettingsItem, position: Int) {
71+
when (position) {
72+
0 -> viewModel.doWhenImportUserParamsPressed()
73+
1 -> viewModel.doWhenExportUserParamsPressed()
74+
2 -> viewModel.doWhenBackupPressed()
75+
3 -> viewModel.doWhenRestorePressed()
76+
}
77+
}
78+
79+
private fun observeUi() {
80+
viewModel.viewEffect.observe(this) {
81+
when (it) {
82+
is ExportOptionsViewEffect.ImportUserParams -> requestImportFile(RC_IMPORT_USER_PARAMS)
83+
is ExportOptionsViewEffect.ExportUserParams -> requestExportFile(RC_EXPORT_USER_PARAMS)
84+
is ExportOptionsViewEffect.RestoreRuntimeParams -> requestImportFile(RC_RESTORE_PARAMS)
85+
is ExportOptionsViewEffect.BackupRuntimeParams -> requestExportFile(RC_BACKUP_PARAMS)
86+
is ExportOptionsViewEffect.ShowImportError -> showErrorDialog(it.messageRes)
87+
is ExportOptionsViewEffect.ShowImportSuccess -> showSuccessDialog(
88+
getString(R.string.import_success_message, it.paramCount)
89+
)
90+
is ExportOptionsViewEffect.ShowExportError -> showErrorDialog(it.messageRes)
91+
is ExportOptionsViewEffect.ShowExportSuccess -> showSuccessDialog(
92+
getString(R.string.export_success_message)
93+
)
94+
}
95+
}
96+
97+
viewModel.viewState.observe(this) {
98+
binding.progress.visibility = if (it.isLoading) View.VISIBLE else View.GONE
99+
binding.loadingText.visibility = if (it.isLoading) View.VISIBLE else View.GONE
100+
binding.recyclerView.visibility = if (it.isLoading) View.GONE else View.VISIBLE
101+
}
102+
}
103+
104+
private fun requestImportFile(requestCode: Int) {
105+
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
106+
addCategory(Intent.CATEGORY_OPENABLE)
107+
type = "*/*"
108+
}
109+
startActivityForResult(intent, requestCode)
110+
}
111+
112+
private fun requestExportFile(requestCode: Int) {
113+
val extension = if (requestCode == RC_BACKUP_PARAMS) "conf" else "json"
114+
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
115+
addCategory(Intent.CATEGORY_OPENABLE)
116+
type = "*/*"
117+
putExtra(Intent.EXTRA_TITLE, "params.$extension")
118+
}
119+
startActivityForResult(intent, requestCode)
120+
}
121+
122+
private fun showErrorDialog(@StringRes messageRes: Int) {
123+
MaterialAlertDialogBuilder(this)
124+
.setTitle(R.string.error)
125+
.setMessage(messageRes)
126+
.setIcon(R.drawable.ic_close)
127+
.create()
128+
.also { dialog ->
129+
if (!isFinishing) dialog.show()
130+
}
131+
}
132+
133+
private fun showSuccessDialog(message: String) {
134+
MaterialAlertDialogBuilder(this)
135+
.setTitle(R.string.done)
136+
.setMessage(message)
137+
.setIcon(R.drawable.ic_check)
138+
.create()
139+
.also { dialog ->
140+
if (!isFinishing) dialog.show()
141+
}
142+
}
143+
144+
companion object {
145+
private const val RC_IMPORT_USER_PARAMS: Int = 1
146+
private const val RC_EXPORT_USER_PARAMS: Int = 2
147+
private const val RC_BACKUP_PARAMS: Int = 3
148+
private const val RC_RESTORE_PARAMS: Int = 4
149+
}
150+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.androidvip.sysctlgui.ui.export
2+
3+
import android.view.LayoutInflater
4+
import android.view.ViewGroup
5+
import androidx.recyclerview.widget.ListAdapter
6+
import com.androidvip.sysctlgui.data.models.SettingsItem
7+
import com.androidvip.sysctlgui.databinding.ListItemSettingsBinding
8+
import com.androidvip.sysctlgui.helpers.OnSettingsItemClickedListener
9+
import com.androidvip.sysctlgui.helpers.SettingsItemDiffCallback
10+
import com.androidvip.sysctlgui.ui.base.BaseViewHolder
11+
12+
class ExportOptionsItemAdapter(
13+
private val itemClickedListener: OnSettingsItemClickedListener
14+
) : ListAdapter<SettingsItem, BaseViewHolder<*>>(SettingsItemDiffCallback) {
15+
16+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeItemViewHolder {
17+
val inflater = LayoutInflater.from(parent.context)
18+
val binding = ListItemSettingsBinding.inflate(
19+
inflater, parent, false
20+
)
21+
return HomeItemViewHolder(binding)
22+
}
23+
24+
override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
25+
if (holder is HomeItemViewHolder) {
26+
holder.bind(getItem(position), position)
27+
}
28+
}
29+
30+
inner class HomeItemViewHolder(
31+
private val binding: ListItemSettingsBinding
32+
) : BaseViewHolder<SettingsItem>(binding) {
33+
override fun bind(item: SettingsItem, position: Int) {
34+
binding.item = item
35+
binding.position = position
36+
binding.onSettingsItemClickedListener = itemClickedListener
37+
binding.executePendingBindings()
38+
}
39+
}
40+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.androidvip.sysctlgui.ui.export
2+
3+
import androidx.annotation.StringRes
4+
5+
sealed interface ExportOptionsViewEffect {
6+
object ImportUserParams : ExportOptionsViewEffect
7+
object ExportUserParams : ExportOptionsViewEffect
8+
9+
object BackupRuntimeParams : ExportOptionsViewEffect
10+
object RestoreRuntimeParams : ExportOptionsViewEffect
11+
12+
class ShowImportError(@StringRes val messageRes: Int) : ExportOptionsViewEffect
13+
class ShowImportSuccess(val paramCount: Int) : ExportOptionsViewEffect
14+
15+
class ShowExportError(@StringRes val messageRes: Int) : ExportOptionsViewEffect
16+
object ShowExportSuccess : ExportOptionsViewEffect
17+
}

0 commit comments

Comments
 (0)