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", "SystemUIUnfoldLib",
"androidx.dynamicanimation_dynamicanimation", "androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures", "androidx.concurrent_concurrent-futures",
"gson-prebuilt-jar",
"dagger2", "dagger2",
"jsr330", "jsr330",
], ],

View File

@@ -14,24 +14,42 @@
package com.android.systemui.shared.clocks package com.android.systemui.shared.clocks
import android.content.Context import android.content.Context
import android.database.ContentObserver
import android.graphics.drawable.Drawable 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 android.util.Log
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.PluginListener import com.android.systemui.plugins.PluginListener
import com.android.systemui.shared.plugins.PluginManager import com.android.systemui.shared.plugins.PluginManager
import com.google.gson.Gson
import javax.inject.Inject import javax.inject.Inject
private val TAG = ClockRegistry::class.simpleName 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 typealias ClockChangeListener = () -> Unit
/** ClockRegistry aggregates providers and plugins */ /** ClockRegistry aggregates providers and plugins */
class ClockRegistry @Inject constructor( open class ClockRegistry @Inject constructor(
val context: Context, 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) { override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) {
val currentId = currentClockId
for (clock in plugin.getClocks()) { for (clock in plugin.getClocks()) {
val id = clock.clockId val id = clock.clockId
val current = availableClocks[id] val current = availableClocks[id]
@@ -42,21 +60,48 @@ class ClockRegistry @Inject constructor(
} }
availableClocks[id] = ClockInfo(clock, plugin) 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) { override fun onPluginDisconnected(plugin: ClockProviderPlugin) {
val currentId = currentClockId
for (clock in plugin.getClocks()) { for (clock in plugin.getClocks()) {
availableClocks.remove(clock.clockId) 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 { init {
pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java) 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 } 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 createExampleClock(clockId: ClockId): Clock? = createClock(clockId)
fun getCurrentClock(): Clock { fun registerClockChangeListener(listener: ClockChangeListener) =
val clockId = "" // TODO: Load setting clockChangeListeners.add(listener)
fun unregisterClockChangeListener(listener: ClockChangeListener) =
clockChangeListeners.remove(listener)
fun createCurrentClock(): Clock {
val clockId = currentClockId
if (!clockId.isNullOrEmpty()) { if (!clockId.isNullOrEmpty()) {
val clock = createClock(clockId) val clock = createClock(clockId)
if (clock != null) { if (clock != null) {
@@ -83,12 +134,13 @@ class ClockRegistry @Inject constructor(
private fun createClock(clockId: ClockId): Clock? = private fun createClock(clockId: ClockId): Clock? =
availableClocks[clockId]?.provider?.createClock(clockId) availableClocks[clockId]?.provider?.createClock(clockId)
fun setCurrentClock(clockId: ClockId) {
// TODO: Write Setting
}
private data class ClockInfo( private data class ClockInfo(
val metadata: ClockMetadata, val metadata: ClockMetadata,
val provider: ClockProvider 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 package com.android.systemui.shared.clocks
import org.mockito.Mockito.`when` as whenever
import android.content.Context import android.content.Context
import android.content.ContentResolver
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Handler
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
@@ -43,17 +46,23 @@ class ClockRegistryTest : SysuiTestCase() {
@Mock private lateinit var mockPluginManager: PluginManager @Mock private lateinit var mockPluginManager: PluginManager
@Mock private lateinit var mockClock: Clock @Mock private lateinit var mockClock: Clock
@Mock private lateinit var mockThumbnail: Drawable @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 pluginListener: PluginListener<ClockProviderPlugin>
private lateinit var registry: ClockRegistry private lateinit var registry: ClockRegistry
private fun failFactory(): Clock { private var settingValue: String = ""
fail("Unexpected call to createClock")
return null!!
}
private fun failThumbnail(): Drawable? { companion object {
fail("Unexpected call to getThumbnail") private fun failFactory(): Clock {
return null fail("Unexpected call to createClock")
return null!!
}
private fun failThumbnail(): Drawable? {
fail("Unexpected call to getThumbnail")
return null
}
} }
private class FakeClockPlugin : ClockProviderPlugin { private class FakeClockPlugin : ClockProviderPlugin {
@@ -68,8 +77,8 @@ class ClockRegistryTest : SysuiTestCase() {
fun addClock( fun addClock(
id: ClockId, id: ClockId,
name: String, name: String,
create: () -> Clock, create: () -> Clock = ::failFactory,
getThumbnail: () -> Drawable? getThumbnail: () -> Drawable? = ::failThumbnail
) { ) {
metadata.add(ClockMetadata(id, name)) metadata.add(ClockMetadata(id, name))
createCallbacks[id] = create createCallbacks[id] = create
@@ -79,8 +88,14 @@ class ClockRegistryTest : SysuiTestCase() {
@Before @Before
fun setUp() { fun setUp() {
whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
val captor = argumentCaptor<PluginListener<ClockProviderPlugin>>() 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(), verify(mockPluginManager).addPluginListener(captor.capture(),
eq(ClockProviderPlugin::class.java)) eq(ClockProviderPlugin::class.java))
pluginListener = captor.value pluginListener = captor.value
@@ -89,12 +104,12 @@ class ClockRegistryTest : SysuiTestCase() {
@Test @Test
fun pluginRegistration_CorrectState() { fun pluginRegistration_CorrectState() {
val plugin1 = FakeClockPlugin() val plugin1 = FakeClockPlugin()
plugin1.addClock("clock_1", "clock 1", ::failFactory, ::failThumbnail) plugin1.addClock("clock_1", "clock 1")
plugin1.addClock("clock_2", "clock 2", ::failFactory, ::failThumbnail) plugin1.addClock("clock_2", "clock 2")
val plugin2 = FakeClockPlugin() val plugin2 = FakeClockPlugin()
plugin2.addClock("clock_3", "clock 3", ::failFactory, ::failThumbnail) plugin2.addClock("clock_3", "clock 3")
plugin2.addClock("clock_4", "clock 4", ::failFactory, ::failThumbnail) plugin2.addClock("clock_4", "clock 4")
pluginListener.onPluginConnected(plugin1, mockContext) pluginListener.onPluginConnected(plugin1, mockContext)
pluginListener.onPluginConnected(plugin2, mockContext) pluginListener.onPluginConnected(plugin2, mockContext)
@@ -114,8 +129,8 @@ class ClockRegistryTest : SysuiTestCase() {
plugin1.addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail }) plugin1.addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail })
val plugin2 = FakeClockPlugin() val plugin2 = FakeClockPlugin()
plugin2.addClock("clock_1", "clock 1", ::failFactory, ::failThumbnail) plugin2.addClock("clock_1", "clock 1")
plugin2.addClock("clock_2", "clock 2", ::failFactory, ::failThumbnail) plugin2.addClock("clock_2", "clock 2")
pluginListener.onPluginConnected(plugin1, mockContext) pluginListener.onPluginConnected(plugin1, mockContext)
pluginListener.onPluginConnected(plugin2, mockContext) pluginListener.onPluginConnected(plugin2, mockContext)
@@ -130,4 +145,65 @@ class ClockRegistryTest : SysuiTestCase() {
assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail) assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail)
assertEquals(registry.getClockThumbnail("clock_2"), 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)
}
} }