Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +19 −1 Original line number Diff line number Diff line Loading @@ -14,6 +14,9 @@ package com.android.systemui.plugins.qs; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.content.Context; import android.graphics.drawable.Drawable; import android.metrics.LogMaker; Loading @@ -25,6 +28,7 @@ import com.android.systemui.plugins.qs.QSTile.Callback; import com.android.systemui.plugins.qs.QSTile.Icon; import com.android.systemui.plugins.qs.QSTile.State; import java.lang.annotation.Retention; import java.util.Objects; import java.util.function.Supplier; Loading Loading @@ -72,6 +76,17 @@ public interface QSTile { return logMaker; } @Retention(SOURCE) @IntDef({COLOR_TILE_ACCENT, COLOR_TILE_RED, COLOR_TILE_BLUE, COLOR_TILE_YELLOW, COLOR_TILE_GREEN}) @interface ColorTile {} int COLOR_TILE_ACCENT = 0; int COLOR_TILE_RED = 1; int COLOR_TILE_BLUE = 2; int COLOR_TILE_YELLOW = 3; int COLOR_TILE_GREEN = 4; default void setColor(@ColorTile int color) {} @ProvidesInterface(version = Callback.VERSION) public interface Callback { public static final int VERSION = 1; Loading Loading @@ -118,6 +133,7 @@ public interface QSTile { public SlashState slash; public boolean handlesLongClick = true; public boolean showRippleEffect = true; public int colorActive = -1; public boolean copyTo(State other) { if (other == null) throw new IllegalArgumentException(); Loading @@ -137,7 +153,8 @@ public interface QSTile { || !Objects.equals(other.dualTarget, dualTarget) || !Objects.equals(other.slash, slash) || !Objects.equals(other.handlesLongClick, handlesLongClick) || !Objects.equals(other.showRippleEffect, showRippleEffect); || !Objects.equals(other.showRippleEffect, showRippleEffect) || !Objects.equals(other.colorActive, colorActive); other.icon = icon; other.iconSupplier = iconSupplier; other.label = label; Loading @@ -152,6 +169,7 @@ public interface QSTile { other.slash = slash != null ? slash.copy() : null; other.handlesLongClick = handlesLongClick; other.showRippleEffect = showRippleEffect; other.colorActive = colorActive; return changed; } Loading packages/SystemUI/res/values-night/colors.xml +5 −0 Original line number Diff line number Diff line Loading @@ -83,4 +83,9 @@ <color name="biometric_dialog_accent">#ff80cbc4</color> <!-- light teal --> <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 --> <color name="GM2_green_500">#FF41Af6A</color> <color name="GM2_blue_500">#5195EA</color> <color name="GM2_red_500">#E25142</color> <color name="GM2_yellow_500">#F5C518</color> </resources> packages/SystemUI/res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -204,4 +204,6 @@ <color name="GM2_yellow_500">#FFFBBC04</color> <color name="GM2_green_500">#FF34A853</color> <color name="GM2_blue_500">#FF4285F4</color> </resources> packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt 0 → 100644 +140 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.systemui.qs import android.content.ContentResolver import android.database.ContentObserver import android.net.Uri import android.os.Handler import android.os.Looper import android.provider.Settings import android.util.Log import com.android.systemui.plugins.qs.QSTile private const val TAG = "QSColorController" private const val QS_COLOR_ICON = "qs_color_icon" private const val QS_COLOR_ENABLED = "qs_color_enabled" private const val QS_COLOR_OVERRIDDEN_TILES = "qs_color_overridden_tiles" private val qsColorIconUri = Settings.System.getUriFor(QS_COLOR_ICON) private val qsColorEnabledUri = Settings.System.getUriFor(QS_COLOR_ENABLED) class QSColorController private constructor() { private var overrideColor = false private var colorIcon = false private lateinit var colorCache: SettingBackedMap companion object { val instance = QSColorController() internal fun overrideColor() = instance.overrideColor internal fun colorIcon() = instance.colorIcon @QSTile.ColorTile private fun getColorFromSetting(setting: String): Int { return when (setting.toLowerCase()) { "red" -> QSTile.COLOR_TILE_RED "blue" -> QSTile.COLOR_TILE_BLUE "green" -> QSTile.COLOR_TILE_GREEN "yellow" -> QSTile.COLOR_TILE_YELLOW else -> QSTile.COLOR_TILE_ACCENT } } } private fun getBooleanSetting(key: String, default: Boolean = false): Boolean = try { Settings.System.getInt(contentResolver, key) != 0 } catch (_: Settings.SettingNotFoundException) { default } private lateinit var tileHost: QSHost private lateinit var contentResolver: ContentResolver fun initQSTileHost(host: QSHost) { tileHost = host contentResolver = tileHost.context.contentResolver colorCache = SettingBackedMap(contentResolver, mutableMapOf()) colorIcon = getBooleanSetting(QS_COLOR_ICON) overrideColor = getBooleanSetting(QS_COLOR_ENABLED) readExistingSettings() contentResolver.registerContentObserver(qsColorEnabledUri, true, settingsListener) contentResolver.registerContentObserver(qsColorIconUri, false, settingsListener) } private fun readExistingSettings() { Settings.System.getString(contentResolver, QS_COLOR_OVERRIDDEN_TILES)?.split(",") ?.mapNotNull { spec -> Settings.System.getString(contentResolver, "$QS_COLOR_ENABLED/$spec")?.let { spec to it } }?.forEach { modifyTileColor(it.first, getColorFromSetting(it.second)) } } private val settingsListener = object : ContentObserver(Handler(Looper.getMainLooper())) { override fun onChange(selfChange: Boolean, uri: Uri) { super.onChange(selfChange, uri) when (uri) { qsColorIconUri -> colorIcon = getBooleanSetting(QS_COLOR_ICON) qsColorEnabledUri -> overrideColor = getBooleanSetting(QS_COLOR_ENABLED) else -> { uri.path?.drop("/system/".length)?.let { val color = getColorFromSetting( Settings.System.getString(contentResolver, it) ?: "accent") val tileSpec = uri.lastPathSegment ?: "" modifyTileColor(tileSpec, color) } } } } } private fun modifyTileColor(spec: String, @QSTile.ColorTile color: Int) { Log.w(TAG, "Setting color of tile $spec to $color") colorCache.put(spec, color) tileHost.tiles.firstOrNull { it.tileSpec == spec }?.setColor(color) } fun applyColorToTile(tile: QSTile) { colorCache.get(tile.tileSpec)?.let { modifyTileColor(tile.tileSpec, it) } } fun applyColorToAllTiles() = tileHost.tiles.forEach(::applyColorToTile) fun destroy() { contentResolver.unregisterContentObserver(settingsListener) } class SettingBackedMap( private val contentResolver: ContentResolver, private val map: MutableMap<String, Int> ) : MutableMap<String, @QSTile.ColorTile Int> by map { override fun put(key: String, @QSTile.ColorTile value: Int): Int? { return map.put(key, value).also { Settings.System.putString(contentResolver, QS_COLOR_OVERRIDDEN_TILES, map.filterValues { it != QSTile.COLOR_TILE_ACCENT } .keys .joinToString(",")) } } } } fun overrideColor() = QSColorController.overrideColor() fun colorIcon() = QSColorController.colorIcon() No newline at end of file packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +8 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D private int mCurrentUser; private StatusBar mStatusBar; private QSColorController mQSColorController = QSColorController.Companion.getInstance(); @Inject public QSTileHost(Context context, StatusBarIconController iconController, Loading Loading @@ -119,6 +121,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D // AutoTileManager can modify mTiles so make sure mTiles has already been initialized. mAutoTiles = autoTiles.get(); }); mQSColorController.initQSTileHost(this); } public StatusBarIconController getIconController() { Loading @@ -132,6 +136,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mServices.destroy(); mPluginManager.removePluginListener(this); mDumpController.unregisterDumpable(this); mQSColorController.destroy(); } @Override Loading Loading @@ -275,6 +281,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mCallbacks.get(i).onTilesChanged(); } } mQSColorController.applyColorToAllTiles(); } @Override Loading Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +19 −1 Original line number Diff line number Diff line Loading @@ -14,6 +14,9 @@ package com.android.systemui.plugins.qs; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.content.Context; import android.graphics.drawable.Drawable; import android.metrics.LogMaker; Loading @@ -25,6 +28,7 @@ import com.android.systemui.plugins.qs.QSTile.Callback; import com.android.systemui.plugins.qs.QSTile.Icon; import com.android.systemui.plugins.qs.QSTile.State; import java.lang.annotation.Retention; import java.util.Objects; import java.util.function.Supplier; Loading Loading @@ -72,6 +76,17 @@ public interface QSTile { return logMaker; } @Retention(SOURCE) @IntDef({COLOR_TILE_ACCENT, COLOR_TILE_RED, COLOR_TILE_BLUE, COLOR_TILE_YELLOW, COLOR_TILE_GREEN}) @interface ColorTile {} int COLOR_TILE_ACCENT = 0; int COLOR_TILE_RED = 1; int COLOR_TILE_BLUE = 2; int COLOR_TILE_YELLOW = 3; int COLOR_TILE_GREEN = 4; default void setColor(@ColorTile int color) {} @ProvidesInterface(version = Callback.VERSION) public interface Callback { public static final int VERSION = 1; Loading Loading @@ -118,6 +133,7 @@ public interface QSTile { public SlashState slash; public boolean handlesLongClick = true; public boolean showRippleEffect = true; public int colorActive = -1; public boolean copyTo(State other) { if (other == null) throw new IllegalArgumentException(); Loading @@ -137,7 +153,8 @@ public interface QSTile { || !Objects.equals(other.dualTarget, dualTarget) || !Objects.equals(other.slash, slash) || !Objects.equals(other.handlesLongClick, handlesLongClick) || !Objects.equals(other.showRippleEffect, showRippleEffect); || !Objects.equals(other.showRippleEffect, showRippleEffect) || !Objects.equals(other.colorActive, colorActive); other.icon = icon; other.iconSupplier = iconSupplier; other.label = label; Loading @@ -152,6 +169,7 @@ public interface QSTile { other.slash = slash != null ? slash.copy() : null; other.handlesLongClick = handlesLongClick; other.showRippleEffect = showRippleEffect; other.colorActive = colorActive; return changed; } Loading
packages/SystemUI/res/values-night/colors.xml +5 −0 Original line number Diff line number Diff line Loading @@ -83,4 +83,9 @@ <color name="biometric_dialog_accent">#ff80cbc4</color> <!-- light teal --> <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 --> <color name="GM2_green_500">#FF41Af6A</color> <color name="GM2_blue_500">#5195EA</color> <color name="GM2_red_500">#E25142</color> <color name="GM2_yellow_500">#F5C518</color> </resources>
packages/SystemUI/res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -204,4 +204,6 @@ <color name="GM2_yellow_500">#FFFBBC04</color> <color name="GM2_green_500">#FF34A853</color> <color name="GM2_blue_500">#FF4285F4</color> </resources>
packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt 0 → 100644 +140 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.systemui.qs import android.content.ContentResolver import android.database.ContentObserver import android.net.Uri import android.os.Handler import android.os.Looper import android.provider.Settings import android.util.Log import com.android.systemui.plugins.qs.QSTile private const val TAG = "QSColorController" private const val QS_COLOR_ICON = "qs_color_icon" private const val QS_COLOR_ENABLED = "qs_color_enabled" private const val QS_COLOR_OVERRIDDEN_TILES = "qs_color_overridden_tiles" private val qsColorIconUri = Settings.System.getUriFor(QS_COLOR_ICON) private val qsColorEnabledUri = Settings.System.getUriFor(QS_COLOR_ENABLED) class QSColorController private constructor() { private var overrideColor = false private var colorIcon = false private lateinit var colorCache: SettingBackedMap companion object { val instance = QSColorController() internal fun overrideColor() = instance.overrideColor internal fun colorIcon() = instance.colorIcon @QSTile.ColorTile private fun getColorFromSetting(setting: String): Int { return when (setting.toLowerCase()) { "red" -> QSTile.COLOR_TILE_RED "blue" -> QSTile.COLOR_TILE_BLUE "green" -> QSTile.COLOR_TILE_GREEN "yellow" -> QSTile.COLOR_TILE_YELLOW else -> QSTile.COLOR_TILE_ACCENT } } } private fun getBooleanSetting(key: String, default: Boolean = false): Boolean = try { Settings.System.getInt(contentResolver, key) != 0 } catch (_: Settings.SettingNotFoundException) { default } private lateinit var tileHost: QSHost private lateinit var contentResolver: ContentResolver fun initQSTileHost(host: QSHost) { tileHost = host contentResolver = tileHost.context.contentResolver colorCache = SettingBackedMap(contentResolver, mutableMapOf()) colorIcon = getBooleanSetting(QS_COLOR_ICON) overrideColor = getBooleanSetting(QS_COLOR_ENABLED) readExistingSettings() contentResolver.registerContentObserver(qsColorEnabledUri, true, settingsListener) contentResolver.registerContentObserver(qsColorIconUri, false, settingsListener) } private fun readExistingSettings() { Settings.System.getString(contentResolver, QS_COLOR_OVERRIDDEN_TILES)?.split(",") ?.mapNotNull { spec -> Settings.System.getString(contentResolver, "$QS_COLOR_ENABLED/$spec")?.let { spec to it } }?.forEach { modifyTileColor(it.first, getColorFromSetting(it.second)) } } private val settingsListener = object : ContentObserver(Handler(Looper.getMainLooper())) { override fun onChange(selfChange: Boolean, uri: Uri) { super.onChange(selfChange, uri) when (uri) { qsColorIconUri -> colorIcon = getBooleanSetting(QS_COLOR_ICON) qsColorEnabledUri -> overrideColor = getBooleanSetting(QS_COLOR_ENABLED) else -> { uri.path?.drop("/system/".length)?.let { val color = getColorFromSetting( Settings.System.getString(contentResolver, it) ?: "accent") val tileSpec = uri.lastPathSegment ?: "" modifyTileColor(tileSpec, color) } } } } } private fun modifyTileColor(spec: String, @QSTile.ColorTile color: Int) { Log.w(TAG, "Setting color of tile $spec to $color") colorCache.put(spec, color) tileHost.tiles.firstOrNull { it.tileSpec == spec }?.setColor(color) } fun applyColorToTile(tile: QSTile) { colorCache.get(tile.tileSpec)?.let { modifyTileColor(tile.tileSpec, it) } } fun applyColorToAllTiles() = tileHost.tiles.forEach(::applyColorToTile) fun destroy() { contentResolver.unregisterContentObserver(settingsListener) } class SettingBackedMap( private val contentResolver: ContentResolver, private val map: MutableMap<String, Int> ) : MutableMap<String, @QSTile.ColorTile Int> by map { override fun put(key: String, @QSTile.ColorTile value: Int): Int? { return map.put(key, value).also { Settings.System.putString(contentResolver, QS_COLOR_OVERRIDDEN_TILES, map.filterValues { it != QSTile.COLOR_TILE_ACCENT } .keys .joinToString(",")) } } } } fun overrideColor() = QSColorController.overrideColor() fun colorIcon() = QSColorController.colorIcon() No newline at end of file
packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +8 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D private int mCurrentUser; private StatusBar mStatusBar; private QSColorController mQSColorController = QSColorController.Companion.getInstance(); @Inject public QSTileHost(Context context, StatusBarIconController iconController, Loading Loading @@ -119,6 +121,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D // AutoTileManager can modify mTiles so make sure mTiles has already been initialized. mAutoTiles = autoTiles.get(); }); mQSColorController.initQSTileHost(this); } public StatusBarIconController getIconController() { Loading @@ -132,6 +136,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mServices.destroy(); mPluginManager.removePluginListener(this); mDumpController.unregisterDumpable(this); mQSColorController.destroy(); } @Override Loading Loading @@ -275,6 +281,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mCallbacks.get(i).onTilesChanged(); } } mQSColorController.applyColorToAllTiles(); } @Override Loading