Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67ffcf89 authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

Add colors to tiles

Protoype for modifying color of tiles. Provides 2 modes:
qs_color_enabled: allows changing background of tiles
qs_color_icon: allows changing color of icon (only use with
qs_color_enabled)

Enable using:
adb shell settings put system <name> <value>
where name is one of (qs_color_enabled|qs_color_icon) and value is (0|1)

With qs_color_enabled, color of individual tiles can be changed using
adb shell settings put system qs_color_enabled/<spec> <color>
where spec is as defined in QSFactoryImpl and color is one of
(red|green|blue|yellow|accent).

Test: manual

Change-Id: I66096cfa5cb8570074b1547ca3e41bd0888973fa
parent 4617a582
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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;
@@ -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();
@@ -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;
@@ -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;
        }

+5 −0
Original line number Diff line number Diff line
@@ -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>
+2 −0
Original line number Diff line number Diff line
@@ -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>
+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
+8 −0
Original line number Diff line number Diff line
@@ -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,
@@ -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() {
@@ -132,6 +136,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
        mServices.destroy();
        mPluginManager.removePluginListener(this);
        mDumpController.unregisterDumpable(this);

        mQSColorController.destroy();
    }

    @Override
@@ -275,6 +281,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
                mCallbacks.get(i).onTilesChanged();
            }
        }

        mQSColorController.applyColorToAllTiles();
    }

    @Override
Loading