My Goal: Setting night mode through preferences and updating the UI in real time.
So far: Done. BUT when I click back to the headers' preference screen I cannot get back in the different preferences' screens again.
To elaborate: My settings are pretty simple at that point. I'm following the preset set by Android Studio (3.1.4) of SettingsActivity, having a template of AppCompatPreferenceActivity. I have one main screen and two deeper ones.
My first screen has two choices: General and About. Upon selecting General, I load a GeneralPreferenceFragment with one Switch preference, that of "Night Mode". If I set it on, it switches the theme real time and when I go back, it's also done on my first settings' screen.
The problem: When I do change the theme, go back to the main SettingsScreen and I try to revisit either General or About screens, I cannot go deeper any more! If I switch the preference an even number so that I end up to the initial theme, I can visit them like nothing has happened.
SettingsActivity class
class SettingsActivity : AppCompatPreferenceActivity() {
// To be used for live changes in night mode
private var mCurrentNightMode: Int = 0
private val TAG = "SettingsActivity"
private var mThemeId = 0
/**
* Doing this hack to add my own actionbar on top since it wasn't there on its own
*/
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
Log.d(TAG, "onPostCreate")
val root = findViewById<View>(android.R.id.list).parent.parent.parent as LinearLayout
val bar = LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false) as Toolbar
root.addView(bar, 0) // insert at top
bar.setNavigationOnClickListener {finish()}
}
override fun setTheme(resid: Int) {
super.setTheme(resid)
mThemeId = resid
}
override fun onCreate(savedInstanceState: Bundle?) {
if (delegate.applyDayNight() && (mThemeId != 0) ) {
// If DayNight has been applied, we need to re-apply the theme for
// the changes to take effect. On API 23+, we should bypass
// setTheme(), which will no-op if the theme ID is identical to the
// current theme ID.
if (Build.VERSION.SDK_INT >= 23) {
onApplyThemeResource(theme, mThemeId, false);
} else {
setTheme(mThemeId);
}
}
super.onCreate(savedInstanceState)
Log.d("SettingsActivity", "onCreate")
setupActionBar()
// Storing the current value so if we come back we'll check on postResume for any changes
mCurrentNightMode = getCurrentNightMode();
}
// Comparing current and last known night mode value
private fun hasNightModeChanged(): Boolean {
return mCurrentNightMode != getCurrentNightMode()
}
private fun getCurrentNightMode(): Int {
return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
}
override fun onPostResume() {
super.onPostResume()
// Self-explanatory. When I load back into this screen, if there's a change in the theme, load it back!
if(hasNightModeChanged()) {
recreate()
}
}
/**
* Set up the [android.app.ActionBar], if the API is available.
*/
private fun setupActionBar() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
/**
* {@inheritDoc}
*/
override fun onIsMultiPane(): Boolean {
return isXLargeTablet(this)
}
/**
* {@inheritDoc}
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
override fun onBuildHeaders(target: List<PreferenceActivity.Header>) {
loadHeadersFromResource(R.xml.pref_headers, target)
}
/**
* This method stops fragment injection in malicious applications.
* Make sure to deny any unknown fragments here.
*/
override fun isValidFragment(fragmentName: String): Boolean {
return PreferenceFragment::class.java.name == fragmentName
|| GeneralPreferenceFragment::class.java.name == fragmentName
|| DataSyncPreferenceFragment::class.java.name == fragmentName
|| NotificationPreferenceFragment::class.java.name == fragmentName
|| AboutFragment::class.java.name == fragmentName
}
/**
* This fragment shows general preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
class GeneralPreferenceFragment : PreferenceFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.pref_general)
setHasOptionsMenu(true)
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
// to their values. When their values change, their summaries are
// updated to reflect the new value, per the Android Design
// guidelines.
findPreference("night_mode").setOnPreferenceChangeListener { preference, newValue ->
val booleanValue = newValue as Boolean
if(booleanValue) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
}
activity.recreate()
true
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == android.R.id.home) {
startActivity(Intent(activity, SettingsActivity::class.java))
return true
}
return super.onOptionsItemSelected(item)
}
}
.
.
.
companion object {
// (didn't change anything here)
}
The problem occurs I think on my "recreate" calls. It's like the preference list's onItemClickListener is null or something similar.
Can anyone help?
EDIT: Simplified, now all my logic in in SettingsActivity class, I needn't have it in the abstract class