How to know which configuration has been changed on an Activity?
To simplify the problem. You don't have to define configChanges
in AndroidManifest.xml if you don't want to take control yourself when configurations changed.
The way I used is just override the onSaveInstanceState
to store the Configuration before the activity being killed, and then restore it to a property like yours as prevConfig
. Since android.content.res.Configuration
is a kind of Parcelable. It is very easy to put it into the Bundle
by calling putParcelable
and recover it on onCreate
.
By doing so, You will get two Configuration
in onCreate()
. You get the previous one from the Bundle savedInstanceState
and the current the from Activity.getResources().getConfiguration()
. The Configuration
has a public method diff, which we can use it to return a bit mask of the differences between them.
To me, the most important thing is how to distinguish the bit mask easily.
Fortunately, there is a method from the official repo we can referenced. It's static and also public. But we can't call it because of the hide
annotation in JavaDoc. So, my first try was using reflection to invoke the method. It seems like the method is completely removed from the Android runtime due to the exception that occurred. After looking at the source. I just copy it to my own sources. Coping seems like a bad idea. But, again. It's a public static method, So it is not too difficult to copy and easy to maintain.
Here is my solution written in Kotlin.
package com.example.android.configchangeapp
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val config = resources.configuration
val prevConfig = savedInstanceState?.getParcelable<Configuration>("config")
if (prevConfig != null) {
val diff = config.diff(prevConfig)
val diffString = configurationDiffToString(diff)
Log.d(TAG, "diff: $diffString")
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable("config", resources.configuration)
}
companion object {
const val TAG = "MainActivity"
fun configurationDiffToString(diff: Int): String {
val list = ArrayList<String>()
if (diff and ActivityInfo.CONFIG_MCC != 0) {
list.add("CONFIG_MCC")
}
if (diff and ActivityInfo.CONFIG_MNC != 0) {
list.add("CONFIG_MNC")
}
if (diff and ActivityInfo.CONFIG_LOCALE != 0) {
list.add("CONFIG_LOCALE")
}
if (diff and ActivityInfo.CONFIG_TOUCHSCREEN != 0) {
list.add("CONFIG_TOUCHSCREEN")
}
if (diff and ActivityInfo.CONFIG_KEYBOARD != 0) {
list.add("CONFIG_KEYBOARD")
}
if (diff and ActivityInfo.CONFIG_KEYBOARD_HIDDEN != 0) {
list.add("CONFIG_KEYBOARD_HIDDEN")
}
if (diff and ActivityInfo.CONFIG_NAVIGATION != 0) {
list.add("CONFIG_NAVIGATION")
}
if (diff and ActivityInfo.CONFIG_ORIENTATION != 0) {
list.add("CONFIG_ORIENTATION")
}
if (diff and ActivityInfo.CONFIG_SCREEN_LAYOUT != 0) {
list.add("CONFIG_SCREEN_LAYOUT")
}
if (diff and ActivityInfo.CONFIG_COLOR_MODE != 0) {
list.add("CONFIG_COLOR_MODE")
}
if (diff and ActivityInfo.CONFIG_UI_MODE != 0) {
list.add("CONFIG_UI_MODE")
}
if (diff and ActivityInfo.CONFIG_SCREEN_SIZE != 0) {
list.add("CONFIG_SCREEN_SIZE")
}
if (diff and ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE != 0) {
list.add("CONFIG_SMALLEST_SCREEN_SIZE")
}
if (diff and ActivityInfo.CONFIG_DENSITY != 0) {
list.add("CONFIG_DENSITY")
}
if (diff and ActivityInfo.CONFIG_LAYOUT_DIRECTION != 0) {
list.add("CONFIG_LAYOUT_DIRECTION")
}
if (diff and ActivityInfo.CONFIG_FONT_SCALE != 0) {
list.add("CONFIG_FONT_SCALE")
}
if (diff and ActivityInfo.CONFIG_FONT_WEIGHT_ADJUSTMENT != 0) {
list.add("CONFIG_AUTO_BOLD_TEXT")
}
return buildString {
append("{")
append(list.joinToString(", "))
append("}")
}
}
}
}