Setting for Lockscreen Clock
Bug: 229771520
Test: Automated
Change-Id: Ia5b4dfe38d48a41cbfb59109c819647b9e8398cf
(cherry picked from commit e3004a6e5f2e8ef2e70df41bf965ed9be7b1675e)
Merged-In: Ia5b4dfe38d48a41cbfb59109c819647b9e8398cf
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 9f790c6..114ea65 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -50,6 +50,7 @@
"SystemUIUnfoldLib",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
+ "gson-prebuilt-jar",
"dagger2",
"jsr330",
],
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 209b576..3245966 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -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 @@
}
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 @@
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 @@
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
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 5d3c575..aaa2357 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -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,17 +46,23 @@
@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 fun failFactory(): Clock {
- fail("Unexpected call to createClock")
- return null!!
- }
+ private var settingValue: String = ""
- private fun failThumbnail(): Drawable? {
- fail("Unexpected call to getThumbnail")
- return null
+ companion object {
+ private fun failFactory(): Clock {
+ fail("Unexpected call to createClock")
+ return null!!
+ }
+
+ private fun failThumbnail(): Drawable? {
+ fail("Unexpected call to getThumbnail")
+ return null
+ }
}
private class FakeClockPlugin : ClockProviderPlugin {
@@ -68,8 +77,8 @@
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 @@
@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 @@
@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 @@
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 @@
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)
+ }
}