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

Commit f800b3f5 authored by Hawkwood Glazier's avatar Hawkwood Glazier Committed by Android (Google) Code Review
Browse files

Merge "Setting for Lockscreen Clock" into tm-qpr-dev

parents 45605364 81d56df8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ android_library {
        "SystemUIUnfoldLib",
        "androidx.dynamicanimation_dynamicanimation",
        "androidx.concurrent_concurrent-futures",
        "gson-prebuilt-jar",
        "dagger2",
        "jsr330",
    ],
+64 −12
Original line number Diff line number Diff line
@@ -14,24 +14,42 @@
package com.android.systemui.shared.clocks

import android.content.Context
import android.database.ContentObserver
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Handler
import android.os.UserHandle
import android.provider.Settings
import android.util.Log
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.PluginListener
import com.android.systemui.shared.plugins.PluginManager
import com.google.gson.Gson
import javax.inject.Inject

private val TAG = ClockRegistry::class.simpleName
private const val DEFAULT_CLOCK_ID = "DEFAULT"
private val DEBUG = true
const val DEFAULT_CLOCK_ID = "DEFAULT"

typealias ClockChangeListener = () -> Unit

/** ClockRegistry aggregates providers and plugins */
class ClockRegistry @Inject constructor(
open class ClockRegistry @Inject constructor(
    val context: Context,
    val pluginManager: PluginManager
    val pluginManager: PluginManager,
    @Main val handler: Handler
) {
    val pluginListener = object : PluginListener<ClockProviderPlugin> {
    private val gson = Gson()
    private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
    private val clockChangeListeners = mutableListOf<ClockChangeListener>()
    private val settingObserver = object : ContentObserver(handler) {
        override fun onChange(selfChange: Boolean, uris: Collection<Uri>, flags: Int, userId: Int) =
            clockChangeListeners.forEach { it() }
    }

    private val pluginListener = object : PluginListener<ClockProviderPlugin> {
        override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) {
            val currentId = currentClockId
            for (clock in plugin.getClocks()) {
                val id = clock.clockId
                val current = availableClocks[id]
@@ -42,21 +60,48 @@ class ClockRegistry @Inject constructor(
                }

                availableClocks[id] = ClockInfo(clock, plugin)

                if (currentId == id) {
                    if (DEBUG) {
                        Log.i(TAG, "Current clock ($currentId) was connected")
                    }
                    clockChangeListeners.forEach { it() }
                }
            }
        }

        override fun onPluginDisconnected(plugin: ClockProviderPlugin) {
            val currentId = currentClockId
            for (clock in plugin.getClocks()) {
                availableClocks.remove(clock.clockId)

                if (currentId == clock.clockId) {
                    Log.w(TAG, "Current clock ($currentId) was disconnected")
                    clockChangeListeners.forEach { it() }
                }
            }
        }
    }

    private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
    open var currentClockId: ClockId
        get() {
            val json = Settings.Secure.getString(context.contentResolver,
                Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE)
            return gson.fromJson(json, ClockSetting::class.java).clockId
        }
        set(value) {
            val json = gson.toJson(ClockSetting(value, System.currentTimeMillis()))
            Settings.Secure.putString(context.contentResolver,
                Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, json)
        }

    init {
        pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java)
        // TODO: Register Settings ContentObserver
        context.contentResolver.registerContentObserver(
            Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
            false,
            settingObserver,
            UserHandle.USER_ALL)
    }

    fun getClocks(): List<ClockMetadata> = availableClocks.map { (_, clock) -> clock.metadata }
@@ -66,8 +111,14 @@ class ClockRegistry @Inject constructor(

    fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId)

    fun getCurrentClock(): Clock {
        val clockId = "" // TODO: Load setting
    fun registerClockChangeListener(listener: ClockChangeListener) =
        clockChangeListeners.add(listener)

    fun unregisterClockChangeListener(listener: ClockChangeListener) =
        clockChangeListeners.remove(listener)

    fun createCurrentClock(): Clock {
        val clockId = currentClockId
        if (!clockId.isNullOrEmpty()) {
            val clock = createClock(clockId)
            if (clock != null) {
@@ -83,12 +134,13 @@ class ClockRegistry @Inject constructor(
    private fun createClock(clockId: ClockId): Clock? =
        availableClocks[clockId]?.provider?.createClock(clockId)

    fun setCurrentClock(clockId: ClockId) {
        // TODO: Write Setting
    }

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

    private data class ClockSetting(
        val clockId: ClockId,
        val _applied_timestamp: Long
    )
}
 No newline at end of file
+92 −16
Original line number Diff line number Diff line
@@ -15,8 +15,11 @@
 */
package com.android.systemui.shared.clocks

import org.mockito.Mockito.`when` as whenever
import android.content.Context
import android.content.ContentResolver
import android.graphics.drawable.Drawable
import android.os.Handler
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -43,9 +46,14 @@ class ClockRegistryTest : SysuiTestCase() {
    @Mock private lateinit var mockPluginManager: PluginManager
    @Mock private lateinit var mockClock: Clock
    @Mock private lateinit var mockThumbnail: Drawable
    @Mock private lateinit var mockHandler: Handler
    @Mock private lateinit var mockContentResolver: ContentResolver
    private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
    private lateinit var registry: ClockRegistry

    private var settingValue: String = ""

    companion object {
        private fun failFactory(): Clock {
            fail("Unexpected call to createClock")
            return null!!
@@ -55,6 +63,7 @@ class ClockRegistryTest : SysuiTestCase() {
            fail("Unexpected call to getThumbnail")
            return null
        }
    }

    private class FakeClockPlugin : ClockProviderPlugin {
        private val metadata = mutableListOf<ClockMetadata>()
@@ -68,8 +77,8 @@ class ClockRegistryTest : SysuiTestCase() {
        fun addClock(
            id: ClockId,
            name: String,
            create: () -> Clock,
            getThumbnail: () -> Drawable?
            create: () -> Clock = ::failFactory,
            getThumbnail: () -> Drawable? = ::failThumbnail
        ) {
            metadata.add(ClockMetadata(id, name))
            createCallbacks[id] = create
@@ -79,8 +88,14 @@ class ClockRegistryTest : SysuiTestCase() {

    @Before
    fun setUp() {
        whenever(mockContext.contentResolver).thenReturn(mockContentResolver)

        val captor = argumentCaptor<PluginListener<ClockProviderPlugin>>()
        registry = ClockRegistry(mockContext, mockPluginManager)
        registry = object : ClockRegistry(mockContext, mockPluginManager, mockHandler) {
            override var currentClockId: ClockId
                get() = settingValue
                set(value) { settingValue = value }
        }
        verify(mockPluginManager).addPluginListener(captor.capture(),
            eq(ClockProviderPlugin::class.java))
        pluginListener = captor.value
@@ -89,12 +104,12 @@ class ClockRegistryTest : SysuiTestCase() {
    @Test
    fun pluginRegistration_CorrectState() {
        val plugin1 = FakeClockPlugin()
        plugin1.addClock("clock_1", "clock 1", ::failFactory, ::failThumbnail)
        plugin1.addClock("clock_2", "clock 2", ::failFactory, ::failThumbnail)
        plugin1.addClock("clock_1", "clock 1")
        plugin1.addClock("clock_2", "clock 2")

        val plugin2 = FakeClockPlugin()
        plugin2.addClock("clock_3", "clock 3", ::failFactory, ::failThumbnail)
        plugin2.addClock("clock_4", "clock 4", ::failFactory, ::failThumbnail)
        plugin2.addClock("clock_3", "clock 3")
        plugin2.addClock("clock_4", "clock 4")

        pluginListener.onPluginConnected(plugin1, mockContext)
        pluginListener.onPluginConnected(plugin2, mockContext)
@@ -114,8 +129,8 @@ class ClockRegistryTest : SysuiTestCase() {
        plugin1.addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail })

        val plugin2 = FakeClockPlugin()
        plugin2.addClock("clock_1", "clock 1", ::failFactory, ::failThumbnail)
        plugin2.addClock("clock_2", "clock 2", ::failFactory, ::failThumbnail)
        plugin2.addClock("clock_1", "clock 1")
        plugin2.addClock("clock_2", "clock 2")

        pluginListener.onPluginConnected(plugin1, mockContext)
        pluginListener.onPluginConnected(plugin2, mockContext)
@@ -130,4 +145,65 @@ class ClockRegistryTest : SysuiTestCase() {
        assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail)
        assertEquals(registry.getClockThumbnail("clock_2"), mockThumbnail)
    }

    @Test
    fun createCurrentClock_pluginConnected() {
        val plugin1 = FakeClockPlugin()
        plugin1.addClock("clock_1", "clock 1")
        plugin1.addClock("clock_2", "clock 2")

        settingValue = "clock_3"
        val plugin2 = FakeClockPlugin()
        plugin2.addClock("clock_3", "clock 3", { mockClock })
        plugin2.addClock("clock_4", "clock 4")

        pluginListener.onPluginConnected(plugin1, mockContext)
        pluginListener.onPluginConnected(plugin2, mockContext)

        val clock = registry.createCurrentClock()
        assertEquals(clock, mockClock)
    }

    @Test
    fun createDefaultClock_pluginDisconnected() {
        val plugin1 = FakeClockPlugin()
        plugin1.addClock(DEFAULT_CLOCK_ID, "default", { mockClock })
        plugin1.addClock("clock_2", "clock 2")

        settingValue = "clock_3"
        val plugin2 = FakeClockPlugin()
        plugin2.addClock("clock_3", "clock 3")
        plugin2.addClock("clock_4", "clock 4")

        pluginListener.onPluginConnected(plugin1, mockContext)
        pluginListener.onPluginConnected(plugin2, mockContext)
        pluginListener.onPluginDisconnected(plugin2)

        val clock = registry.createCurrentClock()
        assertEquals(clock, mockClock)
    }

    @Test
    fun pluginRemoved_clockChanged() {
        val plugin1 = FakeClockPlugin()
        plugin1.addClock("clock_1", "clock 1")
        plugin1.addClock("clock_2", "clock 2")

        settingValue = "clock_3"
        val plugin2 = FakeClockPlugin()
        plugin2.addClock("clock_3", "clock 3", { mockClock })
        plugin2.addClock("clock_4", "clock 4")

        pluginListener.onPluginConnected(plugin1, mockContext)
        pluginListener.onPluginConnected(plugin2, mockContext)

        var changeCallCount = 0
        registry.registerClockChangeListener({ changeCallCount++ })

        pluginListener.onPluginDisconnected(plugin1)
        assertEquals(0, changeCallCount)

        pluginListener.onPluginDisconnected(plugin2)
        assertEquals(1, changeCallCount)
    }
}