From 249e381a63924e2875530970c75fc1a83061c380 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Tue, 10 Dec 2024 15:19:58 +0800 Subject: [PATCH] [Catalyst] Fix MainSwitchBarPreference NPE when configuration changed When activity is recreated (due to configuration is changed), preference screen is created before SettingsActivity sets view layout and results in NPE. As a workaround, save the states inside MainSwitchBarPreference to fix the problem. Bug: 332201912 Flag: EXEMPT library Test: manual Change-Id: I052cd0a521dac203a5f58963c550911e7a741e6f --- .../widget/MainSwitchBarPreference.kt | 75 ++++++++++++++++++- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/widget/MainSwitchBarPreference.kt b/src/com/android/settings/widget/MainSwitchBarPreference.kt index 5cf96397184..b3b341c75a9 100644 --- a/src/com/android/settings/widget/MainSwitchBarPreference.kt +++ b/src/com/android/settings/widget/MainSwitchBarPreference.kt @@ -17,6 +17,8 @@ package com.android.settings.widget import android.content.Context +import android.os.Parcel +import android.os.Parcelable import android.widget.CompoundButton import android.widget.CompoundButton.OnCheckedChangeListener import androidx.preference.TwoStatePreference @@ -28,24 +30,30 @@ import com.android.settingslib.widget.MainSwitchBar class MainSwitchBarPreference(context: Context, private val metadata: MainSwitchBarMetadata) : TwoStatePreference(context), OnCheckedChangeListener, MainSwitchBar.PreChangeListener { - private val mainSwitchBar: MainSwitchBar = (context as SettingsActivity).switchBar + // main switch bar might be null when configuration is just changed + private val mainSwitchBar: MainSwitchBar? + get() = (context as SettingsActivity).switchBar override fun setTitle(title: CharSequence?) { - mainSwitchBar.setTitle(title) + mainSwitchBar?.setTitle(title) + super.setTitle(title) } override fun setSummary(summary: CharSequence?) { - mainSwitchBar.setSummary(summary) + mainSwitchBar?.setSummary(summary) + super.setSummary(summary) } override fun setEnabled(enabled: Boolean) { - mainSwitchBar.isEnabled = enabled + mainSwitchBar?.isEnabled = enabled + super.setEnabled(enabled) } // Preference.setVisible is final, we cannot override it fun updateVisibility() { // always make preference invisible, the UI visibility is reflected on MainSwitchBar isVisible = false + val mainSwitchBar = mainSwitchBar ?: return if ((metadata as? PreferenceAvailabilityProvider)?.isAvailable(context) != false) { mainSwitchBar.show() } else { @@ -54,6 +62,7 @@ class MainSwitchBarPreference(context: Context, private val metadata: MainSwitch } override fun setChecked(checked: Boolean) { + val mainSwitchBar = mainSwitchBar ?: return // remove listener to update UI only mainSwitchBar.removeOnSwitchChangeListener(this) mainSwitchBar.isChecked = checked @@ -62,6 +71,7 @@ class MainSwitchBarPreference(context: Context, private val metadata: MainSwitch override fun onAttached() { super.onAttached() + val mainSwitchBar = mainSwitchBar!! mainSwitchBar.setPreChangeListener(this) mainSwitchBar.addOnSwitchChangeListener(this) } @@ -76,8 +86,65 @@ class MainSwitchBarPreference(context: Context, private val metadata: MainSwitch } override fun onDetached() { + val mainSwitchBar = mainSwitchBar!! mainSwitchBar.removeOnSwitchChangeListener(this) mainSwitchBar.setPreChangeListener(null) super.onDetached() } + + override fun onSaveInstanceState(): Parcelable = + SavedState(super.onSaveInstanceState()!!).also { + it.isEnabled = isEnabled + it.title = title + it.summary = summary + it.mainSwitchBarState = mainSwitchBar?.onSaveInstanceState() + } + + override fun onRestoreInstanceState(state: Parcelable?) { + val savedState = state as SavedState + super.onRestoreInstanceState(savedState.superState) + isEnabled = savedState.isEnabled + title = savedState.title + summary = savedState.summary + mainSwitchBar?.onRestoreInstanceState(savedState.mainSwitchBarState!!) + } + + private class SavedState : BaseSavedState { + var isEnabled: Boolean = false + var title: CharSequence? = null + var summary: CharSequence? = null + var mainSwitchBarState: Parcelable? = null + + constructor(source: Parcel) : super(source) { + isEnabled = source.readBoolean() + title = source.readCharSequence() + summary = source.readCharSequence() + val stateClass = MainSwitchBar.SavedState::class.java + mainSwitchBarState = source.readParcelable(stateClass.classLoader, stateClass) + } + + constructor(superState: Parcelable) : super(superState) + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeBoolean(isEnabled) + dest.writeCharSequence(title) + dest.writeCharSequence(summary) + dest.writeParcelable(mainSwitchBarState, flags) + } + + companion object { + @JvmField + val CREATOR: Parcelable.Creator = + object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): SavedState { + return SavedState(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } + } }