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

Commit cd549151 authored by Hawkwood Glazier's avatar Hawkwood Glazier
Browse files

Seed color setting to override theme color from WPPG

Bug: 242332478
Fixes: 266700158
Test: atest KeyguardClockSwitchScreenshotTest
Change-Id: I2887054bec077f63001a08318b7077f4fc11b5f8
parent 9e4022e4
Loading
Loading
Loading
Loading
+37 −38
Original line number Diff line number Diff line
@@ -21,15 +21,14 @@ import android.os.Handler
import android.provider.Settings
import android.util.Log
import androidx.annotation.OpenForTesting
import com.android.internal.annotations.Keep
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.ClockSettings
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import org.json.JSONObject

private val TAG = ClockRegistry::class.simpleName
private const val DEBUG = true
@@ -64,34 +63,54 @@ open class ClockRegistry(
            disconnectClocks(plugin)
    }

    open var currentClockId: ClockId
    open var settings: ClockSettings?
        get() {
            return try {
            try {
                val json = Settings.Secure.getString(
                    context.contentResolver,
                    Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE
                )
                if (json == null || json.isEmpty()) {
                    return fallbackClockId
                    return null
                }
                ClockSetting.deserialize(json).clockId
                return ClockSettings.deserialize(json)
            } catch (ex: Exception) {
                Log.e(TAG, "Failed to parse clock setting", ex)
                fallbackClockId
                Log.e(TAG, "Failed to parse clock settings", ex)
                return null
            }
        }
        set(value) {
        protected set(value) {
            try {
                val json = ClockSetting.serialize(ClockSetting(value, System.currentTimeMillis()))
                val json = if (value != null) {
                    value._applied_timestamp = System.currentTimeMillis()
                    ClockSettings.serialize(value)
                } else {
                    ""
                }

                Settings.Secure.putString(
                    context.contentResolver,
                    Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, json
                )
            } catch (ex: Exception) {
                Log.e(TAG, "Failed to set clock setting", ex)
                Log.e(TAG, "Failed to set clock settings", ex)
            }
        }

    private fun mutateSetting(mutator: (ClockSettings) -> Unit) {
        val settings = this.settings ?: ClockSettings()
        mutator(settings)
        this.settings = settings
    }

    var currentClockId: ClockId
        get() = settings?.clockId ?: fallbackClockId
        set(value) { mutateSetting { it.clockId = value } }

    var seedColor: Int?
        get() = settings?.seedColor
        set(value) { mutateSetting { it.seedColor = value } }

    init {
        connectClocks(defaultClockProvider)
        if (!availableClocks.containsKey(DEFAULT_CLOCK_ID)) {
@@ -194,36 +213,16 @@ open class ClockRegistry(
        return createClock(DEFAULT_CLOCK_ID)!!
    }

    private fun createClock(clockId: ClockId): ClockController? =
        availableClocks[clockId]?.provider?.createClock(clockId)
    private fun createClock(clockId: ClockId): ClockController? {
        val settings = this.settings ?: ClockSettings()
        if (clockId != settings.clockId) {
            settings.clockId = clockId
        }
        return availableClocks[clockId]?.provider?.createClock(settings)
    }

    private data class ClockInfo(
        val metadata: ClockMetadata,
        val provider: ClockProvider
    )

    @Keep
    data class ClockSetting(
        val clockId: ClockId,
        val _applied_timestamp: Long?
    ) {
        companion object {
            private val KEY_CLOCK_ID = "clockId"
            private val KEY_TIMESTAMP = "_applied_timestamp"

            fun serialize(setting: ClockSetting): String {
                return JSONObject()
                    .put(KEY_CLOCK_ID, setting.clockId)
                    .put(KEY_TIMESTAMP, setting._applied_timestamp)
                    .toString()
            }

            fun deserialize(jsonStr: String): ClockSetting {
                val json = JSONObject(jsonStr)
                return ClockSetting(
                    json.getString(KEY_CLOCK_ID),
                    if (!json.isNull(KEY_TIMESTAMP)) json.getLong(KEY_TIMESTAMP) else null)
            }
        }
    }
}
+15 −4
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockEvents
import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.plugins.ClockSettings
import com.android.systemui.plugins.log.LogBuffer
import java.io.PrintWriter
import java.util.Locale
@@ -46,6 +47,7 @@ class DefaultClockController(
    ctx: Context,
    private val layoutInflater: LayoutInflater,
    private val resources: Resources,
    private val settings: ClockSettings?,
) : ClockController {
    override val smallClock: DefaultClockFaceController
    override val largeClock: LargeClockFaceController
@@ -66,12 +68,14 @@ class DefaultClockController(
        smallClock =
            DefaultClockFaceController(
                layoutInflater.inflate(R.layout.clock_default_small, parent, false)
                    as AnimatableClockView
                    as AnimatableClockView,
                settings?.seedColor
            )
        largeClock =
            LargeClockFaceController(
                layoutInflater.inflate(R.layout.clock_default_large, parent, false)
                    as AnimatableClockView
                    as AnimatableClockView,
                settings?.seedColor
            )
        clocks = listOf(smallClock.view, largeClock.view)

@@ -91,6 +95,7 @@ class DefaultClockController(

    open inner class DefaultClockFaceController(
        override val view: AnimatableClockView,
        val seedColor: Int?,
    ) : ClockFaceController {

        // MAGENTA is a placeholder, and will be assigned correctly in initialize
@@ -105,6 +110,9 @@ class DefaultClockController(
            }

        init {
            if (seedColor != null) {
                currentColor = seedColor
            }
            view.setColors(currentColor, currentColor)
        }

@@ -132,7 +140,9 @@ class DefaultClockController(

        fun updateColor() {
            val color =
                if (isRegionDark) {
                if (seedColor != null) {
                    seedColor
                } else if (isRegionDark) {
                    resources.getColor(android.R.color.system_accent1_100)
                } else {
                    resources.getColor(android.R.color.system_accent2_600)
@@ -152,7 +162,8 @@ class DefaultClockController(

    inner class LargeClockFaceController(
        view: AnimatableClockView,
    ) : DefaultClockFaceController(view) {
        seedColor: Int?,
    ) : DefaultClockFaceController(view, seedColor) {
        override fun recomputePadding(targetRegion: Rect?) {
            // We center the view within the targetRegion instead of within the parent
            // view by computing the difference and adding that to the padding.
+5 −4
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.plugins.ClockSettings

private val TAG = DefaultClockProvider::class.simpleName
const val DEFAULT_CLOCK_NAME = "Default Clock"
@@ -36,12 +37,12 @@ class DefaultClockProvider constructor(
    override fun getClocks(): List<ClockMetadata> =
        listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))

    override fun createClock(id: ClockId): ClockController {
        if (id != DEFAULT_CLOCK_ID) {
            throw IllegalArgumentException("$id is unsupported by $TAG")
    override fun createClock(settings: ClockSettings): ClockController {
        if (settings.clockId != DEFAULT_CLOCK_ID) {
            throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
        }

        return DefaultClockController(ctx, layoutInflater, resources)
        return DefaultClockController(ctx, layoutInflater, resources, settings)
    }

    override fun getClockThumbnail(id: ClockId): Drawable? {
+45 −2
Original line number Diff line number Diff line
@@ -17,11 +17,13 @@ import android.content.res.Resources
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import com.android.internal.annotations.Keep
import com.android.systemui.plugins.annotations.ProvidesInterface
import com.android.systemui.plugins.log.LogBuffer
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
import org.json.JSONObject

/** Identifies a clock design */
typealias ClockId = String
@@ -41,7 +43,13 @@ interface ClockProvider {
    fun getClocks(): List<ClockMetadata>

    /** Initializes and returns the target clock design */
    fun createClock(id: ClockId): ClockController
    @Deprecated("Use overload with ClockSettings")
    fun createClock(id: ClockId): ClockController {
        return createClock(ClockSettings(id, null, null))
    }

    /** Initializes and returns the target clock design */
    fun createClock(settings: ClockSettings): ClockController

    /** A static thumbnail for rendering in some examples */
    fun getClockThumbnail(id: ClockId): Drawable?
@@ -62,7 +70,11 @@ interface ClockController {
    val animations: ClockAnimations

    /** Initializes various rendering parameters. If never called, provides reasonable defaults. */
    fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
    fun initialize(
        resources: Resources,
        dozeFraction: Float,
        foldFraction: Float,
    ) {
        events.onColorPaletteChanged(resources)
        animations.doze(dozeFraction)
        animations.fold(foldFraction)
@@ -167,3 +179,34 @@ data class ClockMetadata(
    val clockId: ClockId,
    val name: String,
)

/** Structure for keeping clock-specific settings */
@Keep
data class ClockSettings(
    var clockId: ClockId? = null,
    var seedColor: Int? = null,
    var _applied_timestamp: Long? = null,
) {
    companion object {
        private val KEY_CLOCK_ID = "clockId"
        private val KEY_SEED_COLOR = "seedColor"
        private val KEY_TIMESTAMP = "_applied_timestamp"

        fun serialize(setting: ClockSettings): String {
            return JSONObject()
                .put(KEY_CLOCK_ID, setting.clockId)
                .put(KEY_SEED_COLOR, setting.seedColor)
                .put(KEY_TIMESTAMP, setting._applied_timestamp)
                .toString()
        }

        fun deserialize(jsonStr: String): ClockSettings {
            val json = JSONObject(jsonStr)
            return ClockSettings(
                json.getString(KEY_CLOCK_ID),
                if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null,
                if (!json.isNull(KEY_TIMESTAMP)) json.getLong(KEY_TIMESTAMP) else null
            )
        }
    }
}
+18 −16
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.ClockSettings
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.mockito.argumentCaptor
@@ -59,7 +60,7 @@ class ClockRegistryTest : SysuiTestCase() {
    private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
    private lateinit var registry: ClockRegistry

    private var settingValue: String = ""
    private var settingValue: ClockSettings? = null

    companion object {
        private fun failFactory(): ClockController {
@@ -79,7 +80,8 @@ class ClockRegistryTest : SysuiTestCase() {
        private val thumbnailCallbacks = mutableMapOf<ClockId, () -> Drawable?>()

        override fun getClocks() = metadata
        override fun createClock(id: ClockId): ClockController = createCallbacks[id]!!()
        override fun createClock(settings: ClockSettings): ClockController =
            createCallbacks[settings.clockId!!]!!()
        override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!()

        fun addClock(
@@ -110,7 +112,7 @@ class ClockRegistryTest : SysuiTestCase() {
            userHandle = UserHandle.USER_ALL,
            defaultClockProvider = fakeDefaultProvider
        ) {
            override var currentClockId: ClockId
            override var settings: ClockSettings?
                get() = settingValue
                set(value) { settingValue = value }
        }
@@ -185,7 +187,7 @@ class ClockRegistryTest : SysuiTestCase() {
            .addClock("clock_1", "clock 1")
            .addClock("clock_2", "clock 2")

        settingValue = "clock_3"
        settingValue = ClockSettings("clock_3", null, null)
        val plugin2 = FakeClockPlugin()
            .addClock("clock_3", "clock 3", { mockClock })
            .addClock("clock_4", "clock 4")
@@ -203,7 +205,7 @@ class ClockRegistryTest : SysuiTestCase() {
            .addClock("clock_1", "clock 1")
            .addClock("clock_2", "clock 2")

        settingValue = "clock_3"
        settingValue = ClockSettings("clock_3", null, null)
        val plugin2 = FakeClockPlugin()
            .addClock("clock_3", "clock 3")
            .addClock("clock_4", "clock 4")
@@ -222,7 +224,7 @@ class ClockRegistryTest : SysuiTestCase() {
            .addClock("clock_1", "clock 1")
            .addClock("clock_2", "clock 2")

        settingValue = "clock_3"
        settingValue = ClockSettings("clock_3", null, null)
        val plugin2 = FakeClockPlugin()
            .addClock("clock_3", "clock 3", { mockClock })
            .addClock("clock_4", "clock 4")
@@ -242,8 +244,8 @@ class ClockRegistryTest : SysuiTestCase() {

    @Test
    fun jsonDeserialization_gotExpectedObject() {
        val expected = ClockRegistry.ClockSetting("ID", 500)
        val actual = ClockRegistry.ClockSetting.deserialize("""{
        val expected = ClockSettings("ID", null, 500)
        val actual = ClockSettings.deserialize("""{
            "clockId":"ID",
            "_applied_timestamp":500
        }""")
@@ -252,15 +254,15 @@ class ClockRegistryTest : SysuiTestCase() {

    @Test
    fun jsonDeserialization_noTimestamp_gotExpectedObject() {
        val expected = ClockRegistry.ClockSetting("ID", null)
        val actual = ClockRegistry.ClockSetting.deserialize("{\"clockId\":\"ID\"}")
        val expected = ClockSettings("ID", null, null)
        val actual = ClockSettings.deserialize("{\"clockId\":\"ID\"}")
        assertEquals(expected, actual)
    }

    @Test
    fun jsonDeserialization_nullTimestamp_gotExpectedObject() {
        val expected = ClockRegistry.ClockSetting("ID", null)
        val actual = ClockRegistry.ClockSetting.deserialize("""{
        val expected = ClockSettings("ID", null, null)
        val actual = ClockSettings.deserialize("""{
            "clockId":"ID",
            "_applied_timestamp":null
        }""")
@@ -269,22 +271,22 @@ class ClockRegistryTest : SysuiTestCase() {

    @Test(expected = JSONException::class)
    fun jsonDeserialization_noId_threwException() {
        val expected = ClockRegistry.ClockSetting("ID", 500)
        val actual = ClockRegistry.ClockSetting.deserialize("{\"_applied_timestamp\":500}")
        val expected = ClockSettings("ID", null, 500)
        val actual = ClockSettings.deserialize("{\"_applied_timestamp\":500}")
        assertEquals(expected, actual)
    }

    @Test
    fun jsonSerialization_gotExpectedString() {
        val expected = "{\"clockId\":\"ID\",\"_applied_timestamp\":500}"
        val actual = ClockRegistry.ClockSetting.serialize( ClockRegistry.ClockSetting("ID", 500))
        val actual = ClockSettings.serialize(ClockSettings("ID", null, 500))
        assertEquals(expected, actual)
    }

    @Test
    fun jsonSerialization_noTimestamp_gotExpectedString() {
        val expected = "{\"clockId\":\"ID\"}"
        val actual = ClockRegistry.ClockSetting.serialize( ClockRegistry.ClockSetting("ID", null))
        val actual = ClockSettings.serialize(ClockSettings("ID", null, null))
        assertEquals(expected, actual)
    }
}