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

This commit is contained in:
Hawkwood Glazier
2022-07-20 14:27:58 +00:00
committed by Android (Google) Code Review
3 changed files with 157 additions and 28 deletions

View File

@@ -50,6 +50,7 @@ android_library {
"SystemUIUnfoldLib",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
"gson-prebuilt-jar",
"dagger2",
"jsr330",
],

View File

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

View File

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