Added "input" settings item, that opens custom input dialog

Updated order of some items in settings
This commit is contained in:
Viktor De Pasquale 2019-11-29 20:07:33 +01:00
parent 02e323133d
commit dec1094a59
9 changed files with 352 additions and 12 deletions

View File

@ -1,14 +1,22 @@
package com.topjohnwu.magisk.redesign.settings package com.topjohnwu.magisk.redesign.settings
import android.content.Context import android.content.Context
import android.os.Environment
import android.view.LayoutInflater
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding
import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.asTransitive import com.topjohnwu.magisk.utils.asTransitive
import com.topjohnwu.magisk.utils.availableLocales import com.topjohnwu.magisk.utils.availableLocales
import com.topjohnwu.magisk.utils.currentLocale import com.topjohnwu.magisk.utils.currentLocale
import java.io.File
// --- Customization // --- Customization
@ -69,16 +77,61 @@ object Restore : SettingsItem.Blank() {
fun HideOrRestore() = fun HideOrRestore() =
if (get<Context>().packageName == BuildConfig.APPLICATION_ID) Hide else Restore if (get<Context>().packageName == BuildConfig.APPLICATION_ID) Hide else Restore
//todo new dialog object DownloadPath : SettingsItem.Input() {
object DownloadPath override var value: String by dataObservable(Config.downloadPath) { Config.downloadPath = it }
override val title = R.string.settings_download_path_title.asTransitive()
override val intermediate: String?
get() = if (Utils.ensureDownloadPath(result) != null) result else null
var result = value
@Bindable get
set(value) {
field = value
notifyChange(BR.result)
notifyChange(BR.path)
}
val path
@Bindable get() = File(
Environment.getExternalStorageDirectory(),
result
).absolutePath.orEmpty()
//fixme this override fun getView(context: Context) = DialogSettingsDownloadPathBinding
object UpdateChannel : SettingsItem.Selector() { .inflate(LayoutInflater.from(context)).also { it.data = this }.root
override var value by dataObservable(Config.updateChannel) { Config.updateChannel = it }
} }
//fixme new dialog object UpdateChannel : SettingsItem.Selector() {
object UpdateChannelUrl override var value by dataObservable(Config.updateChannel) { Config.updateChannel = it }
override val title = R.string.settings_update_channel_title.asTransitive()
init {
val entries = resources.getStringArray(R.array.update_channel).let {
if (!Utils.isCanary && Config.updateChannel < Config.Value.CANARY_CHANNEL)
it.take(it.size - 2).toTypedArray() else it
}
setValues(
entries,
resources.getStringArray(R.array.value_array)
)
}
}
object UpdateChannelUrl : SettingsItem.Input() {
override val title = R.string.settings_update_custom.asTransitive()
override var value: String by dataObservable(Config.customChannelUrl) {
Config.customChannelUrl = it
}
override val intermediate: String? get() = result
var result = value
@Bindable get
set(value) {
field = value
notifyChange(BR.result)
}
override fun getView(context: Context) = DialogSettingsUpdateChannelBinding
.inflate(LayoutInflater.from(context)).also { it.data = this }.root
}
object UpdateChecker : SettingsItem.Toggle() { object UpdateChecker : SettingsItem.Toggle() {
override val title = R.string.settings_check_update_title.asTransitive() override val title = R.string.settings_check_update_title.asTransitive()

View File

@ -1,5 +1,6 @@
package com.topjohnwu.magisk.redesign.settings package com.topjohnwu.magisk.redesign.settings
import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
@ -8,6 +9,7 @@ import androidx.databinding.Bindable
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.StaggeredGridLayoutManager import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.model.entity.recycler.ObservableItem import com.topjohnwu.magisk.model.entity.recycler.ObservableItem
import com.topjohnwu.magisk.model.events.DieEvent import com.topjohnwu.magisk.model.events.DieEvent
@ -29,10 +31,11 @@ class SettingsViewModel : CompatViewModel(), SettingsItem.Callback {
val itemBinding = itemBindingOf<SettingsItem> { it.bindExtra(BR.callback, this) } val itemBinding = itemBindingOf<SettingsItem> { it.bindExtra(BR.callback, this) }
val items = diffListOf( val items = diffListOf(
Customization, Customization,
Theme, Language, Redesign, Theme, Language, Redesign, DownloadPath,
Manager, Manager,
ClearRepoCache, HideOrRestore(), UpdateChecker, SystemlessHosts, Biometrics, UpdateChannel, UpdateChannelUrl, ClearRepoCache, HideOrRestore(), UpdateChecker,
SystemlessHosts, Biometrics,
Magisk, Magisk,
SafeMode, MagiskHide, SafeMode, MagiskHide,
@ -44,11 +47,23 @@ class SettingsViewModel : CompatViewModel(), SettingsItem.Callback {
override fun onItemPressed(view: View, item: SettingsItem) = when (item) { override fun onItemPressed(view: View, item: SettingsItem) = when (item) {
// use only instances you want, don't declare everything // use only instances you want, don't declare everything
Theme -> Navigation.theme().publish() is Theme -> Navigation.theme().publish()
Redesign -> DieEvent().publish() is Redesign -> DieEvent().publish()
is UpdateChannel -> item.openUrlIfNecessary(view)
else -> Unit else -> Unit
} }
private fun UpdateChannel.openUrlIfNecessary(view: View) {
if (value == Config.Value.CUSTOM_CHANNEL) {
if (UpdateChannelUrl.value.isBlank()) {
UpdateChannelUrl.onPressed(view, this@SettingsViewModel)
}
UpdateChannelUrl.isEnabled = true
} else {
UpdateChannelUrl.isEnabled = false
}
}
} }
sealed class SettingsItem : ObservableItem<SettingsItem>() { sealed class SettingsItem : ObservableItem<SettingsItem>() {
@ -60,6 +75,13 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
@Bindable @Bindable
open val description: TransitiveText = TransitiveText.empty open val description: TransitiveText = TransitiveText.empty
var isEnabled = true
@Bindable get
set(value) {
field = value
notifyChange(BR.enabled)
}
protected open val isFullSpan: Boolean = false protected open val isFullSpan: Boolean = false
@CallSuper @CallSuper
@ -127,6 +149,40 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
} }
abstract class Input : Value<String>(), KoinComponent {
override val layoutRes = R.layout.item_settings_input
protected val resources get() = get<Resources>()
protected abstract val intermediate: String?
override fun onPressed(view: View, callback: Callback) {
MagiskDialog(view.context)
.applyTitle(title.getText(resources))
.applyView(getView(view.context))
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = android.R.string.ok
onClick {
intermediate?.let { result ->
preventDismiss = false
value = result
it.dismiss()
super.onPressed(view, callback)
return@onClick
}
preventDismiss = true
}
}
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}
.reveal()
}
abstract fun getView(context: Context): View
}
abstract class Selector : Value<Int>(), KoinComponent { abstract class Selector : Value<Int>(), KoinComponent {
override val layoutRes = R.layout.item_settings_selector override val layoutRes = R.layout.item_settings_selector

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="data"
type="com.topjohnwu.magisk.redesign.settings.DownloadPath" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_generic">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/dialog_custom_download_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/settings_download_path_message(data.path)}"
android:textAppearance="@style/AppearanceFoundation.Caption"
tools:text="@string/settings_download_path_message" />
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_generic"
android:hint="@string/settings_download_path_title"
app:boxStrokeColor="?colorOnSurfaceVariant"
app:errorTextColor="?colorError"
app:hintEnabled="true"
app:hintTextAppearance="@style/AppearanceFoundation.Tiny"
app:hintTextColor="?colorOnSurfaceVariant">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/dialog_custom_download_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri"
android:text="@={data.result}"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textColor="?colorOnSurface"
tools:text="@tools:sample/lorem" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</layout>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="data"
type="com.topjohnwu.magisk.redesign.settings.UpdateChannelUrl" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_generic">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_generic"
android:hint="@string/settings_update_custom_msg"
app:boxStrokeColor="?colorOnSurfaceVariant"
app:errorTextColor="?colorError"
app:hintEnabled="true"
app:hintTextAppearance="@style/AppearanceFoundation.Tiny"
app:hintTextColor="?colorOnSurfaceVariant">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/dialog_custom_download_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri"
android:text="@={data.result}"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textColor="?colorOnSurface"
tools:text="@tools:sample/lorem" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</layout>

View File

@ -18,6 +18,8 @@
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card" style="@style/WidgetFoundation.Card"
android:layout_width="match_parent" android:layout_width="match_parent"
isEnabled="@{item.enabled}"
android:alpha="@{item.enabled ? 1f : .5f}"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="@{(view) -> callback.onItemPressed(view, item)}" android:onClick="@{(view) -> callback.onItemPressed(view, item)}"
tools:layout_gravity="center"> tools:layout_gravity="center">

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.topjohnwu.magisk.redesign.settings.SettingsItem.Input" />
<variable
name="callback"
type="com.topjohnwu.magisk.redesign.settings.SettingsItem.Callback" />
</data>
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
isEnabled="@{item.enabled}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="@{item.enabled ? 1f : .5f}"
android:onClick="@{(view) -> item.onPressed(view, callback)}"
tools:layout_gravity="center">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?listPreferredItemHeightSmall"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/input_icon"
style="@style/WidgetFoundation.Icon"
gone="@{item.icon == 0}"
android:background="@null"
android:rotation="180"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@+id/input_divider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@{item.icon}"
tools:srcCompat="@drawable/ic_fingerprint" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/input_indicator"
style="@style/WidgetFoundation.Icon"
android:background="@null"
android:rotation="180"
app:layout_constraintBottom_toTopOf="@+id/input_divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_back_md2" />
<LinearLayout
android:id="@+id/input_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@{item.icon == 0 ? (int) @dimen/l1 : 0}"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingTop="@dimen/l1"
android:paddingBottom="@dimen/l1"
app:layout_constraintBottom_toTopOf="@+id/input_divider"
app:layout_constraintEnd_toStartOf="@+id/input_indicator"
app:layout_constraintStart_toEndOf="@+id/input_icon"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/input_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="start"
android:text="@{item.title}"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textStyle="bold"
tools:lines="1"
tools:text="@tools:sample/lorem/random" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/input_description"
gone="@{item.description.empty}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{item.description}"
android:textAppearance="@style/AppearanceFoundation.Tiny.Variant"
tools:lines="2"
tools:text="@tools:sample/lorem/random" />
</LinearLayout>
<View
android:id="@+id/input_divider"
gone="@{item.value.empty}"
android:layout_width="match_parent"
android:layout_height="0dp"
android:alpha=".5"
android:background="?colorSurface"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/input_text" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/input_selection"
gone="@{item.value.empty}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/l1"
android:paddingTop="@dimen/l_75"
android:paddingEnd="@dimen/l1"
android:paddingBottom="@dimen/l_75"
android:text="@{item.value}"
android:textAppearance="@style/AppearanceFoundation.Tiny.Variant"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/input_text"
app:layout_constraintTop_toTopOf="@+id/input_divider"
tools:text="@tools:sample/lorem" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>

View File

@ -19,6 +19,8 @@
style="@style/WidgetFoundation.Card" style="@style/WidgetFoundation.Card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
isEnabled="@{item.enabled}"
android:alpha="@{item.enabled ? 1f : .5f}"
android:onClick="@{(view) -> item.onPressed(view, callback)}" android:onClick="@{(view) -> item.onPressed(view, callback)}"
tools:layout_gravity="center"> tools:layout_gravity="center">

View File

@ -20,6 +20,8 @@
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card" style="@style/WidgetFoundation.Card"
android:layout_width="match_parent" android:layout_width="match_parent"
isEnabled="@{item.enabled}"
android:alpha="@{item.enabled ? 1f : .5f}"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="@{(view) -> item.onPressed(view, callback)}" android:onClick="@{(view) -> item.onPressed(view, callback)}"
tools:layout_gravity="center"> tools:layout_gravity="center">
@ -49,6 +51,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/l_25" android:layout_marginEnd="@dimen/l_25"
android:checked="@{item.value}" android:checked="@{item.value}"
isEnabled="@{item.enabled}"
android:text="" android:text=""
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@ -137,7 +137,7 @@
<string name="settings_update_channel_title">Update Channel</string> <string name="settings_update_channel_title">Update Channel</string>
<string name="settings_update_stable">Stable</string> <string name="settings_update_stable">Stable</string>
<string name="settings_update_beta">Beta</string> <string name="settings_update_beta">Beta</string>
<string name="settings_update_custom">Custom</string> <string name="settings_update_custom">Custom Channel</string>
<string name="settings_update_custom_msg">Insert a custom URL</string> <string name="settings_update_custom_msg">Insert a custom URL</string>
<string name="settings_core_only_title">Magisk Core Only Mode</string> <string name="settings_core_only_title">Magisk Core Only Mode</string>
<string name="settings_core_only_summary">Enable only core features. MagiskSU and MagiskHide will still be enabled, but no modules will be loaded</string> <string name="settings_core_only_summary">Enable only core features. MagiskSU and MagiskHide will still be enabled, but no modules will be loaded</string>