blob: 424acfd9916b3aa8d448ae9397526f9ed713d022 [file] [log] [blame]
Jason Monkae7ced22018-08-22 16:56:58 -04001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui.statusbar.phone
16
17import android.content.Context
18import android.content.pm.ActivityInfo
19import android.content.res.Configuration
20import android.os.LocaleList
21
22import com.android.systemui.ConfigurationChangedReceiver
23import com.android.systemui.statusbar.policy.ConfigurationController
24
25import java.util.ArrayList
26
27class ConfigurationControllerImpl(context: Context)
28 : ConfigurationController, ConfigurationChangedReceiver {
29
30 private val listeners: MutableList<ConfigurationController.ConfigurationListener> = ArrayList()
31 private val lastConfig = Configuration()
32 private var density: Int = 0
33 private var fontScale: Float = 0.toFloat()
34 private val inCarMode: Boolean
35 private var uiMode: Int = 0
36 private var localeList: LocaleList? = null
Lucas Dupinfae63a22018-09-18 10:55:15 -070037 private val context: Context
Jason Monkae7ced22018-08-22 16:56:58 -040038
39 init {
40 val currentConfig = context.resources.configuration
Lucas Dupinfae63a22018-09-18 10:55:15 -070041 this.context = context
Jason Monkae7ced22018-08-22 16:56:58 -040042 fontScale = currentConfig.fontScale
43 density = currentConfig.densityDpi
44 inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
45 Configuration.UI_MODE_TYPE_CAR
46 uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
47 localeList = currentConfig.locales
48 }
49
50 override fun notifyThemeChanged() {
51 val listeners = ArrayList(listeners)
52
53 listeners.filterForEach({ this.listeners.contains(it) }) {
54 it.onThemeChanged()
55 }
56 }
57
58 override fun onConfigurationChanged(newConfig: Configuration) {
59 // Avoid concurrent modification exception
60 val listeners = ArrayList(listeners)
61
62 listeners.filterForEach({ this.listeners.contains(it) }) {
63 it.onConfigChanged(newConfig)
64 }
65 val fontScale = newConfig.fontScale
66 val density = newConfig.densityDpi
67 val uiMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
68 val uiModeChanged = uiMode != this.uiMode
69 if (density != this.density || fontScale != this.fontScale ||
70 inCarMode && uiModeChanged) {
71 listeners.filterForEach({ this.listeners.contains(it) }) {
72 it.onDensityOrFontScaleChanged()
73 }
74 this.density = density
75 this.fontScale = fontScale
76 }
77
78 val localeList = newConfig.locales
79 if (localeList != this.localeList) {
80 this.localeList = localeList
81 listeners.filterForEach({ this.listeners.contains(it) }) {
82 it.onLocaleListChanged()
83 }
84 }
85
86 if (uiModeChanged) {
Lucas Dupinfae63a22018-09-18 10:55:15 -070087 // We need to force the style re-evaluation to make sure that it's up to date
88 // and attrs were reloaded.
89 context.theme.applyStyle(context.themeResId, true)
90
Jason Monkae7ced22018-08-22 16:56:58 -040091 this.uiMode = uiMode
92 listeners.filterForEach({ this.listeners.contains(it) }) {
93 it.onUiModeChanged()
94 }
95 }
96
97 if (lastConfig.updateFrom(newConfig) and ActivityInfo.CONFIG_ASSETS_PATHS != 0) {
98 listeners.filterForEach({ this.listeners.contains(it) }) {
99 it.onOverlayChanged()
100 }
101 }
102 }
103
104 override fun addCallback(listener: ConfigurationController.ConfigurationListener) {
105 listeners.add(listener)
106 listener.onDensityOrFontScaleChanged()
107 }
108
109 override fun removeCallback(listener: ConfigurationController.ConfigurationListener) {
110 listeners.remove(listener)
111 }
112}
113
114// This could be done with a Collection.filter and Collection.forEach, but Collection.filter
115// creates a new array to store them in and we really don't need that here, so this provides
116// a little more optimized inline version.
117inline fun <T> Collection<T>.filterForEach(f: (T) -> Boolean, execute: (T) -> Unit) {
118 forEach {
119 if (f.invoke(it)) {
120 execute.invoke(it)
121 }
122 }
123}