Some app features require permissions. If a permission is not granted, we need the preference of it to be "semi" disabled (and not change if it's ticked or not), with a text telling that it cannot be used because there aren't enough granted permissions, so that it only partially looks disabled (the text will stay black, with the warning being red). It won't look like a normal disabled preference.
Like a normal disabled preference, though, when it's in this state, all preferences that depend on it will be disabled and un-clickable.
However, unlike a normal disabled preference, Upon clicking such a preference, it will go to a screen of granting the required permissions.
There are various ways to handle clicking on preferences, but none that I've found to handle clicking on a disabled preference.
Since various other preferences depend on such a preference (so that they get enabled/disabled based on whether it's enabled/disabled), I need to let it become disabled when needed, yet allow to click on it.
I tried various ways to handle clicking:
All of those do not get called when the preference is disabled, and you try to click on the preference.
Is it possible to disable a preference yet still handle clicks on it?
Alternatively, is it possible extend the functionality of the dependencies of the preferences ? To make other preferences to be dependent on this preference, but not just based on whether it's enabled or not ? Meaning they are enabled only if this one is enabled AND it's not in this semi-enabled state?
Currently, only CheckboxPreference is used on the app as the one that needs this new state, so it's easy to just make its checkbox look disabled, yet let the preference itself stay enabled (I just store the CheckBox view by using view.findViewById(android.R.id.checkbox) inside onCreateView method)
OK, I've found a workaround for this:
class CheckboxPreferenceEx : android.preference.CheckBoxPreference {
var alwaysAllowClickingEnabled: Boolean = false
private val onPreferenceViewClickListener: View.OnClickListener
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
init {
onPreferenceViewClickListener = View.OnClickListener {
if (onPreferenceChangeListener != null && !isEnabled)
if (onPreferenceChangeListener.onPreferenceChange(this@CheckboxPreferenceEx, !isChecked))
isChecked = !isChecked
if (this@CheckboxPreferenceEx.isEnabled)
this@CheckboxPreferenceEx.onClick()
}
}
override fun onCreateView(parent: ViewGroup): View {
val view = super.onCreateView(parent)
setPreferenceTitleTextViewToHaveMultipleLines(view)
//mTitleTextView = (TextView) view.findViewById(android.R.id.title);
//mSummaryTextView = (TextView) view.findViewById(android.R.id.summary);
return view
}
override fun onBindView(view: View) {
super.onBindView(view)
view.setOnClickListener(onPreferenceViewClickListener)
val dependency = dependency
if (!TextUtils.isEmpty(dependency) && !isEnabled) {
//if the reason this preference is disabled is because of dependency, act as normal
val dependencyPreference = preferenceManager.findPreference(dependency)
if (!dependencyPreference!!.isEnabled || dependencyPreference is TwoStatePreference && !dependencyPreference.isChecked)
return
}
if (alwaysAllowClickingEnabled) {
//we chose to enable all except for the checkbox, which will still look disabled
view.isEnabled = true
setEnabledToViewAndItsDescendants(view, true)
}
}
companion object {
fun setPreferenceTitleTextViewToHaveMultipleLines(v: View) {
if (v is TextView && v.getId() == android.R.id.title) {
v.setSingleLine(false)
return
}
if (v is ViewGroup) {
for (i in 0 until v.childCount) {
val child = v.getChildAt(i)
setPreferenceTitleTextViewToHaveMultipleLines(child)
}
}
}
fun setEnabledToViewAndItsDescendants(v: View?, enabled: Boolean) {
if (v == null)
return
v.isEnabled = enabled
if (v is ViewGroup) {
val viewGroups = ArrayList<ViewGroup>()
viewGroups.add(v)
while (!viewGroups.isEmpty()) {
val viewGroup = viewGroups.removeAt(0)
val childCount = viewGroup.childCount
for (i in 0 until childCount) {
val childView = viewGroup.getChildAt(i)
childView.isEnabled = enabled
if (childView is ViewGroup)
viewGroups.add(childView)
}
}
}
}
}
}
Having such a preference like that, when I called setAlwaysAllowClickingEnabled(true) on it, will cause the preference to really be disabled, yet when you click on it, it will call the callback.
In our case, we wanted to let the checkbox stay disabled, yet enable the title and summary TextViews, but you can do as you wish, depending on your requirements. Updated the code.