mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-24 03:37:58 +00:00
Updated settings to level functionality with the legacy
This commit is contained in:
parent
89da45f9ac
commit
3efea47ca8
@ -23,7 +23,7 @@ val redesignModule = module {
|
||||
viewModel { ModuleViewModel(get(), get(), get()) }
|
||||
viewModel { RequestViewModel() }
|
||||
viewModel { SafetynetViewModel(get()) }
|
||||
viewModel { SettingsViewModel() }
|
||||
viewModel { SettingsViewModel(get()) }
|
||||
viewModel { SuperuserViewModel(get(), get(), get()) }
|
||||
viewModel { ThemeViewModel() }
|
||||
viewModel { InstallViewModel() }
|
||||
|
@ -19,4 +19,9 @@ class SettingsFragment : CompatFragment<SettingsViewModel, FragmentSettingsMd2Bi
|
||||
activity.title = resources.getString(R.string.section_settings)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.items.forEach { it.refresh() }
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +1,19 @@
|
||||
package com.topjohnwu.magisk.redesign.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.Toast
|
||||
import androidx.databinding.Bindable
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.Config
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.*
|
||||
import com.topjohnwu.magisk.databinding.DialogSettingsAppNameBinding
|
||||
import com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding
|
||||
import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
|
||||
import com.topjohnwu.magisk.extensions.get
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.asTransitive
|
||||
import com.topjohnwu.magisk.utils.availableLocales
|
||||
import com.topjohnwu.magisk.utils.currentLocale
|
||||
import com.topjohnwu.magisk.utils.*
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import java.io.File
|
||||
|
||||
// --- Customization
|
||||
@ -27,6 +25,7 @@ object Customization : SettingsItem.Section() {
|
||||
object Language : SettingsItem.Selector() {
|
||||
override var value by dataObservable(0) {
|
||||
Config.locale = entryValues.getOrNull(it)?.toString() ?: return@dataObservable
|
||||
refreshLocale()
|
||||
}
|
||||
|
||||
override val title = R.string.language.asTransitive()
|
||||
@ -61,11 +60,34 @@ object Manager : SettingsItem.Section() {
|
||||
object ClearRepoCache : SettingsItem.Blank() {
|
||||
override val title = R.string.settings_clear_cache_title.asTransitive()
|
||||
override val description = R.string.settings_clear_cache_summary.asTransitive()
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Info.env.isActive
|
||||
}
|
||||
}
|
||||
|
||||
object Hide : SettingsItem.Blank() {
|
||||
object Hide : SettingsItem.Input() {
|
||||
override val title = R.string.settings_hide_manager_title.asTransitive()
|
||||
override val description = R.string.settings_hide_manager_summary.asTransitive()
|
||||
override val showStrip = false
|
||||
override var value: String = resources.getString(R.string.re_app_name)
|
||||
set(value) {
|
||||
field = value
|
||||
notifyChange(BR.value)
|
||||
notifyChange(BR.error)
|
||||
}
|
||||
val isError
|
||||
@Bindable get() = value.length > 14 || value.isBlank()
|
||||
|
||||
override val intermediate: String?
|
||||
get() = if (isError) null else value
|
||||
|
||||
override fun getView(context: Context) = DialogSettingsAppNameBinding
|
||||
.inflate(LayoutInflater.from(context)).also { it.data = this }.root
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Info.env.isActive
|
||||
}
|
||||
}
|
||||
|
||||
object Restore : SettingsItem.Blank() {
|
||||
@ -129,11 +151,7 @@ object UpdateChannelUrl : SettingsItem.Input() {
|
||||
notifyChange(BR.result)
|
||||
}
|
||||
|
||||
init {
|
||||
updateState()
|
||||
}
|
||||
|
||||
fun updateState() {
|
||||
override fun refresh() {
|
||||
isEnabled = UpdateChannel.value == Config.Value.CUSTOM_CHANNEL
|
||||
}
|
||||
|
||||
@ -144,19 +162,43 @@ object UpdateChannelUrl : SettingsItem.Input() {
|
||||
object UpdateChecker : SettingsItem.Toggle() {
|
||||
override val title = R.string.settings_check_update_title.asTransitive()
|
||||
override val description = R.string.settings_check_update_summary.asTransitive()
|
||||
override var value by dataObservable(Config.checkUpdate) { Config.checkUpdate = it }
|
||||
override var value by dataObservable(Config.checkUpdate) {
|
||||
Config.checkUpdate = it
|
||||
Utils.scheduleUpdateCheck(get())
|
||||
}
|
||||
}
|
||||
|
||||
// check whether is module already installed beforehand?
|
||||
object SystemlessHosts : SettingsItem.Blank() {
|
||||
override val title = R.string.settings_hosts_title.asTransitive()
|
||||
override val description = R.string.settings_hosts_summary.asTransitive()
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Info.env.isActive
|
||||
}
|
||||
}
|
||||
|
||||
object Biometrics : SettingsItem.Toggle() {
|
||||
override val title = R.string.settings_su_biometric_title.asTransitive()
|
||||
override val description = R.string.settings_su_biometric_summary.asTransitive()
|
||||
override var value by dataObservable(Config.suBiometric) { Config.suBiometric = it }
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = BiometricHelper.isSupported && Utils.showSuperUser()
|
||||
if (!isEnabled) {
|
||||
value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Reauthenticate : SettingsItem.Toggle() {
|
||||
override val title = R.string.settings_su_reauth_title.asTransitive()
|
||||
override val description = R.string.settings_su_reauth_summary.asTransitive()
|
||||
override var value by dataObservable(Config.suReAuth) { Config.suReAuth = it }
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Utils.showSuperUser()
|
||||
}
|
||||
}
|
||||
|
||||
// --- Magisk
|
||||
@ -168,13 +210,35 @@ object Magisk : SettingsItem.Section() {
|
||||
object SafeMode : SettingsItem.Toggle() {
|
||||
override val title = R.string.settings_core_only_title.asTransitive()
|
||||
override val description = R.string.settings_core_only_summary.asTransitive()
|
||||
override var value by dataObservable(Config.coreOnly) { Config.coreOnly = it }
|
||||
override var value by dataObservable(Config.coreOnly) {
|
||||
if (Config.coreOnly == it) return@dataObservable
|
||||
Config.coreOnly = it
|
||||
when {
|
||||
it -> runCatching { Const.MAGISK_DISABLE_FILE.createNewFile() }
|
||||
else -> Const.MAGISK_DISABLE_FILE.delete()
|
||||
}
|
||||
Utils.toast(R.string.settings_reboot_toast, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Info.env.isActive
|
||||
}
|
||||
}
|
||||
|
||||
object MagiskHide : SettingsItem.Toggle() {
|
||||
override val title = R.string.magiskhide.asTransitive()
|
||||
override val description = R.string.settings_magiskhide_summary.asTransitive()
|
||||
override var value by dataObservable(Config.magiskHide) { Config.magiskHide = it }
|
||||
override var value by dataObservable(Config.magiskHide) {
|
||||
Config.magiskHide = it
|
||||
when {
|
||||
it -> Shell.su("magiskhide --enable").submit()
|
||||
else -> Shell.su("magiskhide --disable").submit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Info.env.isActive
|
||||
}
|
||||
}
|
||||
|
||||
// --- Superuser
|
||||
@ -197,6 +261,10 @@ object AccessMode : SettingsItem.Selector() {
|
||||
resources.getStringArray(R.array.value_array)
|
||||
)
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Utils.showSuperUser()
|
||||
}
|
||||
}
|
||||
|
||||
object MultiuserMode : SettingsItem.Selector() {
|
||||
@ -213,6 +281,10 @@ object MultiuserMode : SettingsItem.Selector() {
|
||||
resources.getStringArray(R.array.value_array)
|
||||
)
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Const.USER_ID <= 0 && Utils.showSuperUser()
|
||||
}
|
||||
}
|
||||
|
||||
object MountNamespaceMode : SettingsItem.Selector() {
|
||||
@ -229,6 +301,10 @@ object MountNamespaceMode : SettingsItem.Selector() {
|
||||
resources.getStringArray(R.array.value_array)
|
||||
)
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Utils.showSuperUser()
|
||||
}
|
||||
}
|
||||
|
||||
object AutomaticResponse : SettingsItem.Selector() {
|
||||
@ -245,6 +321,10 @@ object AutomaticResponse : SettingsItem.Selector() {
|
||||
resources.getStringArray(R.array.value_array)
|
||||
)
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Utils.showSuperUser()
|
||||
}
|
||||
}
|
||||
|
||||
object RequestTimeout : SettingsItem.Selector() {
|
||||
@ -263,6 +343,10 @@ object RequestTimeout : SettingsItem.Selector() {
|
||||
val currentValue = Config.suDefaultTimeout.toString()
|
||||
value = entryValues.indexOfFirst { it == currentValue }
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Utils.showSuperUser()
|
||||
}
|
||||
}
|
||||
|
||||
object SUNotification : SettingsItem.Selector() {
|
||||
@ -279,4 +363,8 @@ object SUNotification : SettingsItem.Selector() {
|
||||
resources.getStringArray(R.array.value_array)
|
||||
)
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Utils.showSuperUser()
|
||||
}
|
||||
}
|
@ -1,30 +1,47 @@
|
||||
package com.topjohnwu.magisk.redesign.settings
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.data.database.RepoDao
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.model.download.DownloadService
|
||||
import com.topjohnwu.magisk.model.entity.internal.Configuration
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
|
||||
import com.topjohnwu.magisk.model.entity.recycler.ObservableItem
|
||||
import com.topjohnwu.magisk.model.events.DieEvent
|
||||
import com.topjohnwu.magisk.model.events.PermissionEvent
|
||||
import com.topjohnwu.magisk.model.events.RecreateEvent
|
||||
import com.topjohnwu.magisk.model.events.dialog.BiometricDialog
|
||||
import com.topjohnwu.magisk.model.navigation.Navigation
|
||||
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
|
||||
import com.topjohnwu.magisk.redesign.home.itemBindingOf
|
||||
import com.topjohnwu.magisk.redesign.module.adapterOf
|
||||
import com.topjohnwu.magisk.redesign.superuser.diffListOf
|
||||
import com.topjohnwu.magisk.utils.PatchAPK
|
||||
import com.topjohnwu.magisk.utils.TransitiveText
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.get
|
||||
import kotlin.properties.ObservableProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class SettingsViewModel : CompatViewModel(), SettingsItem.Callback {
|
||||
class SettingsViewModel(
|
||||
private val repositoryDao: RepoDao
|
||||
) : CompatViewModel(), SettingsItem.Callback {
|
||||
|
||||
val adapter = adapterOf<SettingsItem>()
|
||||
val itemBinding = itemBindingOf<SettingsItem> { it.bindExtra(BR.callback, this) }
|
||||
@ -34,10 +51,10 @@ class SettingsViewModel : CompatViewModel(), SettingsItem.Callback {
|
||||
|
||||
Manager,
|
||||
UpdateChannel, UpdateChannelUrl, ClearRepoCache, HideOrRestore(), UpdateChecker,
|
||||
SystemlessHosts, Biometrics,
|
||||
Biometrics, Reauthenticate,
|
||||
|
||||
Magisk,
|
||||
SafeMode, MagiskHide,
|
||||
SafeMode, MagiskHide, SystemlessHosts,
|
||||
|
||||
Superuser,
|
||||
AccessMode, MultiuserMode, MountNamespaceMode, AutomaticResponse, RequestTimeout,
|
||||
@ -45,20 +62,74 @@ class SettingsViewModel : CompatViewModel(), SettingsItem.Callback {
|
||||
)
|
||||
|
||||
override fun onItemPressed(view: View, item: SettingsItem) = when (item) {
|
||||
is DownloadPath -> requireRWPermission()
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
override fun onItemChanged(view: View, item: SettingsItem) = when (item) {
|
||||
// use only instances you want, don't declare everything
|
||||
is Theme -> Navigation.theme().publish()
|
||||
is Redesign -> DieEvent().publish()
|
||||
is Language -> RecreateEvent().publish()
|
||||
|
||||
is UpdateChannel -> openUrlIfNecessary(view)
|
||||
is Biometrics -> authenticateOrRevert()
|
||||
is ClearRepoCache -> clearRepoCache()
|
||||
is SystemlessHosts -> createHosts()
|
||||
is Hide -> updateManager(hide = true)
|
||||
is Restore -> updateManager(hide = false)
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
private fun openUrlIfNecessary(view: View) {
|
||||
UpdateChannelUrl.updateState()
|
||||
UpdateChannelUrl.refresh()
|
||||
if (UpdateChannelUrl.value.isBlank()) {
|
||||
UpdateChannelUrl.onPressed(view, this@SettingsViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun authenticateOrRevert() {
|
||||
// immediately revert the preference
|
||||
Biometrics.value = !Biometrics.value
|
||||
BiometricDialog {
|
||||
// allow the change on success
|
||||
onSuccess { Biometrics.value = !Biometrics.value }
|
||||
}.publish()
|
||||
}
|
||||
|
||||
private fun clearRepoCache() {
|
||||
Completable.fromAction { repositoryDao.clear() }
|
||||
.subscribeK { Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT) }
|
||||
}
|
||||
|
||||
private fun createHosts() {
|
||||
Shell.su("add_hosts_module").submit {
|
||||
Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requireRWPermission() {
|
||||
val callback = PublishSubject.create<Boolean>()
|
||||
callback.subscribeK { if (!it) requireRWPermission() }
|
||||
PermissionEvent(
|
||||
listOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
), callback
|
||||
).publish()
|
||||
}
|
||||
|
||||
private fun updateManager(hide: Boolean) {
|
||||
if (hide) {
|
||||
PatchAPK.hideManager(get(), Hide.value)
|
||||
} else {
|
||||
DownloadService(get()) {
|
||||
subject = DownloadSubject.Manager(Configuration.APK.Restore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sealed class SettingsItem : ObservableItem<SettingsItem>() {
|
||||
@ -81,7 +152,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
|
||||
|
||||
@CallSuper
|
||||
open fun onPressed(view: View, callback: Callback) {
|
||||
callback.onItemPressed(view, this)
|
||||
callback.onItemChanged(view, this)
|
||||
|
||||
// notify only after the callback invocation; callback can invalidate the backing data,
|
||||
// which wouldn't be recognized with reverse approach
|
||||
@ -90,6 +161,8 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
|
||||
notifyChange(BR.description)
|
||||
}
|
||||
|
||||
open fun refresh() {}
|
||||
|
||||
override fun onBindingBound(binding: ViewDataBinding) {
|
||||
super.onBindingBound(binding)
|
||||
if (isFullSpan) {
|
||||
@ -105,6 +178,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
|
||||
|
||||
interface Callback {
|
||||
fun onItemPressed(view: View, item: SettingsItem)
|
||||
fun onItemChanged(view: View, item: SettingsItem)
|
||||
}
|
||||
|
||||
// ---
|
||||
@ -131,6 +205,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
|
||||
override val layoutRes = R.layout.item_settings_toggle
|
||||
|
||||
override fun onPressed(view: View, callback: Callback) {
|
||||
callback.onItemPressed(view, this)
|
||||
value = !value
|
||||
super.onPressed(view, callback)
|
||||
}
|
||||
@ -147,11 +222,13 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
|
||||
abstract class Input : Value<String>(), KoinComponent {
|
||||
|
||||
override val layoutRes = R.layout.item_settings_input
|
||||
open val showStrip = true
|
||||
|
||||
protected val resources get() = get<Resources>()
|
||||
protected abstract val intermediate: String?
|
||||
|
||||
override fun onPressed(view: View, callback: Callback) {
|
||||
callback.onItemPressed(view, this)
|
||||
MagiskDialog(view.context)
|
||||
.applyTitle(title.getText(resources))
|
||||
.applyView(getView(view.context))
|
||||
@ -210,6 +287,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
|
||||
|
||||
override fun onPressed(view: View, callback: Callback) {
|
||||
if (entries.isEmpty() || entryValues.isEmpty()) return
|
||||
callback.onItemPressed(view, this)
|
||||
MagiskDialog(view.context)
|
||||
.applyTitle(title.getText(resources))
|
||||
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
@ -229,6 +307,11 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
|
||||
|
||||
override val layoutRes = R.layout.item_settings_blank
|
||||
|
||||
override fun onPressed(view: View, callback: Callback) {
|
||||
callback.onItemPressed(view, this)
|
||||
super.onPressed(view, callback)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class Section : SettingsItem() {
|
||||
|
52
app/src/main/res/layout/dialog_settings_app_name.xml
Normal file
52
app/src/main/res/layout/dialog_settings_app_name.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<?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.Hide" />
|
||||
|
||||
</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_app_name_hint"
|
||||
app:boxStrokeColor="?colorOnSurfaceVariant"
|
||||
app:counterEnabled="true"
|
||||
app:counterMaxLength="14"
|
||||
app:counterOverflowTextColor="@color/colorError"
|
||||
app:error="@{data.error ? @string/settings_app_name_error : @string/empty}"
|
||||
app:errorEnabled="true"
|
||||
app:errorTextColor="?colorError"
|
||||
app:helperText="@string/settings_app_name_helper"
|
||||
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="textCapWords"
|
||||
android:text="@={data.value}"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textColor="?colorOnSurface"
|
||||
tools:text="@tools:sample/lorem" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</layout>
|
@ -21,7 +21,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="@{item.enabled ? 1f : .5f}"
|
||||
android:onClick="@{(view) -> callback.onItemPressed(view, item)}"
|
||||
android:onClick="@{(view) -> callback.onItemChanged(view, item)}"
|
||||
tools:layout_gravity="center">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
@ -92,7 +92,7 @@
|
||||
|
||||
<View
|
||||
android:id="@+id/input_divider"
|
||||
gone="@{item.value.empty}"
|
||||
gone="@{item.value.empty || !item.showStrip}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:alpha=".5"
|
||||
@ -102,7 +102,7 @@
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/input_selection"
|
||||
gone="@{item.value.empty}"
|
||||
gone="@{item.value.empty || !item.showStrip}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/l1"
|
||||
|
Loading…
x
Reference in New Issue
Block a user