mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
fix: save instance state in protected view #2283
This commit is contained in:
@@ -3,8 +3,12 @@ package com.kunzisoft.keepass.view
|
|||||||
import android.view.View.OnClickListener
|
import android.view.View.OnClickListener
|
||||||
|
|
||||||
interface ProtectedFieldView {
|
interface ProtectedFieldView {
|
||||||
fun setProtection(protection: Boolean, onUnprotectClickListener: OnClickListener?)
|
fun setProtection(
|
||||||
|
protection: Boolean,
|
||||||
|
isCurrentlyProtected: Boolean,
|
||||||
|
onUnprotectClickListener: OnClickListener?
|
||||||
|
)
|
||||||
|
fun isCurrentlyProtected(): Boolean
|
||||||
fun protect()
|
fun protect()
|
||||||
fun unprotect()
|
fun unprotect()
|
||||||
fun isCurrentlyProtected(): Boolean
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Jeremy Jamet / Kunzisoft.
|
||||||
|
*
|
||||||
|
* This file is part of KeePassDX.
|
||||||
|
*
|
||||||
|
* KeePassDX 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.
|
||||||
|
*
|
||||||
|
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.kunzisoft.keepass.view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.os.Parcelable.Creator
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import com.kunzisoft.keepass.utils.readBooleanCompat
|
||||||
|
import com.kunzisoft.keepass.utils.writeBooleanCompat
|
||||||
|
|
||||||
|
|
||||||
|
abstract class ProtectedTextFieldView @JvmOverloads constructor(context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyle: Int = 0)
|
||||||
|
: RelativeLayout(context, attrs, defStyle),
|
||||||
|
GenericTextFieldView, ProtectedFieldView {
|
||||||
|
|
||||||
|
var isProtected: Boolean = false
|
||||||
|
private set
|
||||||
|
private var mIsCurrentlyProtected: Boolean = true
|
||||||
|
|
||||||
|
// Only to fix rebuild view from template
|
||||||
|
var onSaveInstanceState: (() -> Unit)? = null
|
||||||
|
|
||||||
|
override fun isCurrentlyProtected(): Boolean {
|
||||||
|
return mIsCurrentlyProtected
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun protect() {
|
||||||
|
mIsCurrentlyProtected = true
|
||||||
|
changeProtectedValueParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unprotect() {
|
||||||
|
mIsCurrentlyProtected = false
|
||||||
|
changeProtectedValueParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setProtection(
|
||||||
|
protection: Boolean,
|
||||||
|
isCurrentlyProtected: Boolean,
|
||||||
|
onUnprotectClickListener: OnClickListener?
|
||||||
|
) {
|
||||||
|
this.isProtected = protection
|
||||||
|
this.mIsCurrentlyProtected = isCurrentlyProtected
|
||||||
|
if (isProtected) {
|
||||||
|
changeProtectedValueParameters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun changeProtectedValueParameters()
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(): Parcelable? {
|
||||||
|
onSaveInstanceState?.invoke()
|
||||||
|
return ProtectionState(super.onSaveInstanceState()).apply {
|
||||||
|
this.isCurrentlyProtected = isCurrentlyProtected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRestoreInstanceState(state: Parcelable?) {
|
||||||
|
when (state) {
|
||||||
|
is ProtectionState -> {
|
||||||
|
super.onRestoreInstanceState(state.superState)
|
||||||
|
mIsCurrentlyProtected = state.isCurrentlyProtected
|
||||||
|
}
|
||||||
|
else -> super.onRestoreInstanceState(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ProtectionState : BaseSavedState {
|
||||||
|
|
||||||
|
var isCurrentlyProtected: Boolean = true
|
||||||
|
|
||||||
|
constructor(superState: Parcelable?) : super(superState)
|
||||||
|
|
||||||
|
private constructor(parcel: Parcel) : super(parcel) {
|
||||||
|
isCurrentlyProtected = parcel.readBooleanCompat()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||||
|
super.writeToParcel(out, flags)
|
||||||
|
out.writeBooleanCompat(isCurrentlyProtected)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Creator<ProtectionState> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): ProtectionState {
|
||||||
|
return ProtectionState(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<ProtectionState?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ import com.kunzisoft.keepass.otp.OtpElement
|
|||||||
import com.kunzisoft.keepass.otp.OtpEntryFields
|
import com.kunzisoft.keepass.otp.OtpEntryFields
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.utils.KeyboardUtil.hideKeyboard
|
import com.kunzisoft.keepass.utils.KeyboardUtil.hideKeyboard
|
||||||
|
import com.kunzisoft.keepass.utils.readListCompat
|
||||||
import com.kunzisoft.keepass.utils.readParcelableCompat
|
import com.kunzisoft.keepass.utils.readParcelableCompat
|
||||||
|
|
||||||
|
|
||||||
@@ -45,6 +46,9 @@ abstract class TemplateAbstractView<
|
|||||||
private var mTemplate: Template? = null
|
private var mTemplate: Template? = null
|
||||||
protected var mEntryInfo: EntryInfo? = null
|
protected var mEntryInfo: EntryInfo? = null
|
||||||
|
|
||||||
|
// To keep unprotected views during orientation change
|
||||||
|
protected var mUnprotectedFields = mutableListOf<Field>()
|
||||||
|
|
||||||
private var mViewFields = mutableListOf<ViewField>()
|
private var mViewFields = mutableListOf<ViewField>()
|
||||||
|
|
||||||
protected var mFontInVisibility: Boolean = PreferencesUtil.fieldFontIsInVisibility(context)
|
protected var mFontInVisibility: Boolean = PreferencesUtil.fieldFontIsInVisibility(context)
|
||||||
@@ -569,7 +573,7 @@ abstract class TemplateAbstractView<
|
|||||||
}
|
}
|
||||||
|
|
||||||
return if (!isStandardFieldName(customField.name)) {
|
return if (!isStandardFieldName(customField.name)) {
|
||||||
customFieldsContainerView.visibility = View.VISIBLE
|
customFieldsContainerView.visibility = VISIBLE
|
||||||
if (getIndexViewFieldByName(customField.name) >= 0) {
|
if (getIndexViewFieldByName(customField.name) >= 0) {
|
||||||
// Update a custom field with a new value,
|
// Update a custom field with a new value,
|
||||||
// new field name must be the same as old field name
|
// new field name must be the same as old field name
|
||||||
@@ -674,6 +678,16 @@ abstract class TemplateAbstractView<
|
|||||||
putCustomField(Field(otpField.name, otpField.protectedValue))
|
putCustomField(Field(otpField.name, otpField.protectedValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveUnprotectedFieldState(field: Field, isCurrentlyProtected: Boolean) {
|
||||||
|
try {
|
||||||
|
if (!isCurrentlyProtected) {
|
||||||
|
mUnprotectedFields.add(field)
|
||||||
|
} else {
|
||||||
|
mUnprotectedFields.remove(field)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onRestoreInstanceState(state: Parcelable?) {
|
override fun onRestoreInstanceState(state: Parcelable?) {
|
||||||
//begin boilerplate code so parent classes can restore state
|
//begin boilerplate code so parent classes can restore state
|
||||||
if (state !is SavedState) {
|
if (state !is SavedState) {
|
||||||
@@ -682,6 +696,7 @@ abstract class TemplateAbstractView<
|
|||||||
} else {
|
} else {
|
||||||
mTemplate = state.template
|
mTemplate = state.template
|
||||||
mEntryInfo = state.entryInfo
|
mEntryInfo = state.entryInfo
|
||||||
|
mUnprotectedFields = state.unprotectedFields
|
||||||
onRestoreEntryInstanceState(state)
|
onRestoreEntryInstanceState(state)
|
||||||
buildTemplateAndPopulateInfo()
|
buildTemplateAndPopulateInfo()
|
||||||
super.onRestoreInstanceState(state.superState)
|
super.onRestoreInstanceState(state.superState)
|
||||||
@@ -697,6 +712,7 @@ abstract class TemplateAbstractView<
|
|||||||
retrieveDefaultValues = false)
|
retrieveDefaultValues = false)
|
||||||
saveState.template = this.mTemplate
|
saveState.template = this.mTemplate
|
||||||
saveState.entryInfo = this.mEntryInfo
|
saveState.entryInfo = this.mEntryInfo
|
||||||
|
saveState.unprotectedFields = this.mUnprotectedFields
|
||||||
onSaveEntryInstanceState(saveState)
|
onSaveEntryInstanceState(saveState)
|
||||||
return saveState
|
return saveState
|
||||||
}
|
}
|
||||||
@@ -706,6 +722,7 @@ abstract class TemplateAbstractView<
|
|||||||
protected class SavedState : BaseSavedState {
|
protected class SavedState : BaseSavedState {
|
||||||
var template: Template? = null
|
var template: Template? = null
|
||||||
var entryInfo: EntryInfo? = null
|
var entryInfo: EntryInfo? = null
|
||||||
|
var unprotectedFields = mutableListOf<Field>()
|
||||||
// TODO Move
|
// TODO Move
|
||||||
var tempDateTimeViewId: Int? = null
|
var tempDateTimeViewId: Int? = null
|
||||||
|
|
||||||
@@ -714,6 +731,7 @@ abstract class TemplateAbstractView<
|
|||||||
private constructor(parcel: Parcel) : super(parcel) {
|
private constructor(parcel: Parcel) : super(parcel) {
|
||||||
template = parcel.readParcelableCompat() ?: template
|
template = parcel.readParcelableCompat() ?: template
|
||||||
entryInfo = parcel.readParcelableCompat() ?: entryInfo
|
entryInfo = parcel.readParcelableCompat() ?: entryInfo
|
||||||
|
parcel.readListCompat<Field>(unprotectedFields)
|
||||||
val dateTimeViewId = parcel.readInt()
|
val dateTimeViewId = parcel.readInt()
|
||||||
if (dateTimeViewId != -1)
|
if (dateTimeViewId != -1)
|
||||||
tempDateTimeViewId = dateTimeViewId
|
tempDateTimeViewId = dateTimeViewId
|
||||||
@@ -723,6 +741,7 @@ abstract class TemplateAbstractView<
|
|||||||
super.writeToParcel(out, flags)
|
super.writeToParcel(out, flags)
|
||||||
out.writeParcelable(template, flags)
|
out.writeParcelable(template, flags)
|
||||||
out.writeParcelable(entryInfo, flags)
|
out.writeParcelable(entryInfo, flags)
|
||||||
|
out.writeList(unprotectedFields)
|
||||||
out.writeInt(tempDateTimeViewId ?: -1)
|
out.writeInt(tempDateTimeViewId ?: -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,9 +127,16 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
PasswordTextEditFieldView(it)
|
PasswordTextEditFieldView(it)
|
||||||
else TextEditFieldView(it)).apply {
|
else TextEditFieldView(it)).apply {
|
||||||
// hiddenProtectedValue (mHideProtectedValue) don't work with TextInputLayout
|
// hiddenProtectedValue (mHideProtectedValue) don't work with TextInputLayout
|
||||||
setProtection(field.protectedValue.isProtected) {
|
setProtection(
|
||||||
|
protection = field.protectedValue.isProtected,
|
||||||
|
isCurrentlyProtected = mUnprotectedFields.contains(field).not()
|
||||||
|
) {
|
||||||
mOnUnprotectClickListener?.invoke(field, this)
|
mOnUnprotectClickListener?.invoke(field, this)
|
||||||
}
|
}
|
||||||
|
// Trick to bypass the onSaveInstanceState in rebuild child
|
||||||
|
onSaveInstanceState = {
|
||||||
|
saveUnprotectedFieldState(field, isCurrentlyProtected())
|
||||||
|
}
|
||||||
default = templateAttribute.default
|
default = templateAttribute.default
|
||||||
setMaxChars(templateAttribute.options.getNumberChars())
|
setMaxChars(templateAttribute.options.getNumberChars())
|
||||||
setMaxLines(templateAttribute.options.getNumberLines())
|
setMaxLines(templateAttribute.options.getNumberLines())
|
||||||
|
|||||||
@@ -63,9 +63,16 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
PasskeyTextFieldView(it)
|
PasskeyTextFieldView(it)
|
||||||
else TextFieldView(it)).apply {
|
else TextFieldView(it)).apply {
|
||||||
applyFontVisibility(mFontInVisibility)
|
applyFontVisibility(mFontInVisibility)
|
||||||
setProtection(field.protectedValue.isProtected) {
|
setProtection(
|
||||||
|
protection = field.protectedValue.isProtected,
|
||||||
|
isCurrentlyProtected = mUnprotectedFields.contains(field).not()
|
||||||
|
) {
|
||||||
mOnUnprotectClickListener?.invoke(this)
|
mOnUnprotectClickListener?.invoke(this)
|
||||||
}
|
}
|
||||||
|
// Trick to bypass the onSaveInstanceState in rebuild child
|
||||||
|
onSaveInstanceState = {
|
||||||
|
saveUnprotectedFieldState(field, isCurrentlyProtected())
|
||||||
|
}
|
||||||
label = templateAttribute.alias
|
label = templateAttribute.alias
|
||||||
?: TemplateField.getLocalizedName(context, field.name)
|
?: TemplateField.getLocalizedName(context, field.name)
|
||||||
setMaxChars(templateAttribute.options.getNumberChars())
|
setMaxChars(templateAttribute.options.getNumberChars())
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import android.util.TypedValue
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.appcompat.view.ContextThemeWrapper
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
import androidx.appcompat.widget.AppCompatImageButton
|
import androidx.appcompat.widget.AppCompatImageButton
|
||||||
@@ -27,8 +26,7 @@ import com.kunzisoft.keepass.R
|
|||||||
open class TextEditFieldView @JvmOverloads constructor(context: Context,
|
open class TextEditFieldView @JvmOverloads constructor(context: Context,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyle: Int = 0)
|
defStyle: Int = 0)
|
||||||
: RelativeLayout(context, attrs, defStyle),
|
: ProtectedTextFieldView(context, attrs, defStyle) {
|
||||||
GenericTextFieldView, ProtectedFieldView {
|
|
||||||
|
|
||||||
private var labelViewId = ViewCompat.generateViewId()
|
private var labelViewId = ViewCompat.generateViewId()
|
||||||
private var valueViewId = ViewCompat.generateViewId()
|
private var valueViewId = ViewCompat.generateViewId()
|
||||||
@@ -171,31 +169,31 @@ open class TextEditFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setProtection(protection: Boolean, onUnprotectClickListener: OnClickListener?) {
|
override fun setProtection(
|
||||||
if (protection) {
|
protection: Boolean,
|
||||||
|
isCurrentlyProtected: Boolean,
|
||||||
|
onUnprotectClickListener: OnClickListener?
|
||||||
|
) {
|
||||||
|
super.setProtection(protection, isCurrentlyProtected, onUnprotectClickListener)
|
||||||
|
if (isProtected) {
|
||||||
labelView.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE
|
labelView.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE
|
||||||
valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
|
||||||
/*
|
|
||||||
// FIXME Called by itself during orientation change
|
// FIXME Called by itself during orientation change
|
||||||
|
/*
|
||||||
labelView.setEndIconOnClickListener {
|
labelView.setEndIconOnClickListener {
|
||||||
onUnprotectClickListener?.onClick(this@TextEditFieldView)
|
onUnprotectClickListener?.onClick(this@TextEditFieldView)
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isCurrentlyProtected(): Boolean {
|
override fun changeProtectedValueParameters() {
|
||||||
return valueView.transformationMethod == PasswordTransformationMethod.getInstance()
|
if (isCurrentlyProtected()) {
|
||||||
}
|
|
||||||
|
|
||||||
override fun protect() {
|
|
||||||
valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||||
valueView.transformationMethod = PasswordTransformationMethod.getInstance()
|
valueView.transformationMethod = PasswordTransformationMethod.getInstance()
|
||||||
}
|
} else {
|
||||||
|
|
||||||
override fun unprotect() {
|
|
||||||
valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||||
valueView.transformationMethod = SingleLineTransformationMethod.getInstance()
|
valueView.transformationMethod = SingleLineTransformationMethod.getInstance()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun setOnActionClickListener(onActionClickListener: OnClickListener?,
|
override fun setOnActionClickListener(onActionClickListener: OnClickListener?,
|
||||||
@DrawableRes actionImageId: Int?) {
|
@DrawableRes actionImageId: Int?) {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import android.util.AttributeSet
|
|||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.ContextThemeWrapper
|
import android.view.ContextThemeWrapper
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.widget.AppCompatImageButton
|
import androidx.appcompat.widget.AppCompatImageButton
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
@@ -42,8 +41,7 @@ import com.kunzisoft.keepass.utils.AppUtil.openExternalApp
|
|||||||
open class TextFieldView @JvmOverloads constructor(context: Context,
|
open class TextFieldView @JvmOverloads constructor(context: Context,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyle: Int = 0)
|
defStyle: Int = 0)
|
||||||
: RelativeLayout(context, attrs, defStyle),
|
: ProtectedTextFieldView(context, attrs, defStyle) {
|
||||||
GenericTextFieldView, ProtectedFieldView {
|
|
||||||
|
|
||||||
protected var labelViewId = ViewCompat.generateViewId()
|
protected var labelViewId = ViewCompat.generateViewId()
|
||||||
private var valueViewId = ViewCompat.generateViewId()
|
private var valueViewId = ViewCompat.generateViewId()
|
||||||
@@ -205,38 +203,29 @@ open class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setProtection(protection: Boolean, onUnprotectClickListener: OnClickListener?) {
|
override fun setProtection(
|
||||||
showButton.isVisible = protection
|
protection: Boolean,
|
||||||
showButton.isSelected = true
|
isCurrentlyProtected: Boolean,
|
||||||
|
onUnprotectClickListener: OnClickListener?
|
||||||
|
) {
|
||||||
|
super.setProtection(protection, isCurrentlyProtected, onUnprotectClickListener)
|
||||||
|
showButton.isVisible = isProtected
|
||||||
|
if (isProtected) {
|
||||||
showButton.setOnClickListener {
|
showButton.setOnClickListener {
|
||||||
onUnprotectClickListener?.onClick(this@TextFieldView)
|
onUnprotectClickListener?.onClick(this@TextFieldView)
|
||||||
}
|
}
|
||||||
changeProtectedValueParameters()
|
}
|
||||||
invalidate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isCurrentlyProtected(): Boolean {
|
override fun changeProtectedValueParameters() {
|
||||||
return showButton.isSelected
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun protect() {
|
|
||||||
showButton.isSelected = !showButton.isSelected
|
|
||||||
changeProtectedValueParameters()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun unprotect() {
|
|
||||||
showButton.isSelected = !showButton.isSelected
|
|
||||||
changeProtectedValueParameters()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun changeProtectedValueParameters() {
|
|
||||||
valueView.apply {
|
valueView.apply {
|
||||||
if (showButton.isVisible) {
|
if (showButton.isVisible) {
|
||||||
applyHiddenStyle(showButton.isSelected)
|
applyHiddenStyle(isCurrentlyProtected())
|
||||||
} else {
|
} else {
|
||||||
linkify()
|
linkify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun linkify() {
|
private fun linkify() {
|
||||||
|
|||||||
Reference in New Issue
Block a user