Files
app_Settings/src/com/android/settings/widget/MainSwitchBarPreference.kt
Jacky Wang 249e381a63 [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
2024-12-10 15:25:39 +08:00

151 lines
5.4 KiB
Kotlin

/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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
import com.android.settings.SettingsActivity
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.widget.MainSwitchBar
/** Preference abstraction of the [MainSwitchBar] in settings activity. */
class MainSwitchBarPreference(context: Context, private val metadata: MainSwitchBarMetadata) :
TwoStatePreference(context), OnCheckedChangeListener, MainSwitchBar.PreChangeListener {
// 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)
super.setTitle(title)
}
override fun setSummary(summary: CharSequence?) {
mainSwitchBar?.setSummary(summary)
super.setSummary(summary)
}
override fun setEnabled(enabled: Boolean) {
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 {
mainSwitchBar.hide()
}
}
override fun setChecked(checked: Boolean) {
val mainSwitchBar = mainSwitchBar ?: return
// remove listener to update UI only
mainSwitchBar.removeOnSwitchChangeListener(this)
mainSwitchBar.isChecked = checked
mainSwitchBar.addOnSwitchChangeListener(this)
}
override fun onAttached() {
super.onAttached()
val mainSwitchBar = mainSwitchBar!!
mainSwitchBar.setPreChangeListener(this)
mainSwitchBar.addOnSwitchChangeListener(this)
}
override fun preChange(isCheck: Boolean) = callChangeListener(isCheck)
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
// prevent user from toggling the switch before data store operation is done
isEnabled = false
// once data store is updated, isEnabled will be reset due to rebind
persistBoolean(isChecked)
}
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<SavedState> =
object : Parcelable.Creator<SavedState> {
override fun createFromParcel(parcel: Parcel): SavedState {
return SavedState(parcel)
}
override fun newArray(size: Int): Array<SavedState?> {
return arrayOfNulls(size)
}
}
}
}
}