Setting for Lockscreen Clock
Bug: 229771520
Test: Automated
Change-Id: Ia5b4dfe38d48a41cbfb59109c819647b9e8398cf
(cherry picked from commit e3004a6e5f)
Merged-In: Ia5b4dfe38d48a41cbfb59109c819647b9e8398cf
This commit is contained in:
@@ -50,6 +50,7 @@ android_library {
|
||||
"SystemUIUnfoldLib",
|
||||
"androidx.dynamicanimation_dynamicanimation",
|
||||
"androidx.concurrent_concurrent-futures",
|
||||
"gson-prebuilt-jar",
|
||||
"dagger2",
|
||||
"jsr330",
|
||||
],
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -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 @@ 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 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 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user