Merge "Initial ClockPlugin interfaces and ClockRegistry functions" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e71aec0ff8
@@ -23,7 +23,9 @@ import java.util.TimeZone;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin used to replace main clock in keyguard.
|
* Plugin used to replace main clock in keyguard.
|
||||||
|
* @deprecated Migrating to ClockProviderPlugin
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION)
|
@ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION)
|
||||||
public interface ClockPlugin extends Plugin {
|
public interface ClockPlugin extends Plugin {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
||||||
|
* except in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the
|
||||||
|
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the specific language governing
|
||||||
|
* permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.systemui.plugins
|
||||||
|
|
||||||
|
import com.android.systemui.plugins.annotations.ProvidesInterface
|
||||||
|
import android.annotation.FloatRange
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
/** Identifies a clock design */
|
||||||
|
typealias ClockId = String
|
||||||
|
|
||||||
|
/** A Plugin which exposes the ClockProvider interface */
|
||||||
|
@ProvidesInterface(action = ClockProviderPlugin.ACTION, version = ClockProviderPlugin.VERSION)
|
||||||
|
interface ClockProviderPlugin : Plugin, ClockProvider {
|
||||||
|
companion object {
|
||||||
|
const val ACTION = "com.android.systemui.action.PLUGIN_CLOCK_PROVIDER"
|
||||||
|
const val VERSION = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Interface for building clocks and providing information about those clocks */
|
||||||
|
interface ClockProvider {
|
||||||
|
/** Returns metadata for all clocks this provider knows about */
|
||||||
|
fun getClocks(): List<ClockMetadata>
|
||||||
|
|
||||||
|
/** Initializes and returns the target clock design */
|
||||||
|
fun createClock(id: ClockId): Clock
|
||||||
|
|
||||||
|
/** A static thumbnail for rendering in some examples */
|
||||||
|
fun getClockThumbnail(id: ClockId): Drawable?
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Interface for controlling an active clock */
|
||||||
|
interface Clock {
|
||||||
|
/** A small version of the clock, appropriate for smaller viewports */
|
||||||
|
val smallClock: View
|
||||||
|
|
||||||
|
/** A large version of the clock, appropriate when a bigger viewport is available */
|
||||||
|
val largeClock: View
|
||||||
|
|
||||||
|
/** Callback to update the clock view to the current time */
|
||||||
|
fun onTimeTick()
|
||||||
|
|
||||||
|
/** Sets the level of the AOD transition */
|
||||||
|
fun setAodFraction(@FloatRange(from = 0.0, to = 1.0) fraction: Float)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Some data about a clock design */
|
||||||
|
data class ClockMetadata(
|
||||||
|
val clockId: ClockId,
|
||||||
|
val name: String
|
||||||
|
)
|
||||||
@@ -53,8 +53,11 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages custom clock faces for AOD and lock screen.
|
* Manages custom clock faces for AOD and lock screen.
|
||||||
|
*
|
||||||
|
* @deprecated Migrate to ClockRegistry
|
||||||
*/
|
*/
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
|
@Deprecated
|
||||||
public final class ClockManager {
|
public final class ClockManager {
|
||||||
|
|
||||||
private static final String TAG = "ClockOptsProvider";
|
private static final String TAG = "ClockOptsProvider";
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
||||||
|
* except in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the
|
||||||
|
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the specific language governing
|
||||||
|
* permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.keyguard.clock
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.Log
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.plugins.Clock
|
||||||
|
import com.android.systemui.plugins.ClockId
|
||||||
|
import com.android.systemui.plugins.ClockMetadata
|
||||||
|
import com.android.systemui.plugins.ClockProvider
|
||||||
|
import com.android.systemui.plugins.ClockProviderPlugin
|
||||||
|
import com.android.systemui.plugins.PluginListener
|
||||||
|
import com.android.systemui.shared.plugins.PluginManager
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val TAG = ClockRegistry::class.simpleName
|
||||||
|
private const val DEFAULT_CLOCK_ID = "DEFAULT"
|
||||||
|
|
||||||
|
typealias ClockChangeListener = () -> Unit
|
||||||
|
|
||||||
|
/** ClockRegistry aggregates providers and plugins */
|
||||||
|
// TODO: Is this the right place for this?
|
||||||
|
@SysUISingleton
|
||||||
|
class ClockRegistry @Inject constructor(
|
||||||
|
val context: Context,
|
||||||
|
val pluginManager: PluginManager
|
||||||
|
) {
|
||||||
|
val pluginListener = object : PluginListener<ClockProviderPlugin> {
|
||||||
|
override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) {
|
||||||
|
for (clock in plugin.getClocks()) {
|
||||||
|
val id = clock.clockId
|
||||||
|
val current = availableClocks[id]
|
||||||
|
if (current != null) {
|
||||||
|
Log.e(TAG, "Clock Id conflict: $id is registered by both " +
|
||||||
|
"${plugin::class.simpleName} and ${current.provider::class.simpleName}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
availableClocks[id] = ClockInfo(clock, plugin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPluginDisconnected(plugin: ClockProviderPlugin) {
|
||||||
|
for (clock in plugin.getClocks()) {
|
||||||
|
availableClocks.remove(clock.clockId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java)
|
||||||
|
// TODO: Register Settings ContentObserver
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getClocks(): List<ClockMetadata> = availableClocks.map { (_, clock) -> clock.metadata }
|
||||||
|
|
||||||
|
fun getClockThumbnail(clockId: ClockId): Drawable? =
|
||||||
|
availableClocks[clockId]?.provider?.getClockThumbnail(clockId)
|
||||||
|
|
||||||
|
fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId)
|
||||||
|
|
||||||
|
fun getCurrentClock(): Clock {
|
||||||
|
val clockId = "" // TODO: Load setting
|
||||||
|
if (!clockId.isNullOrEmpty()) {
|
||||||
|
val clock = createClock(clockId)
|
||||||
|
if (clock != null) {
|
||||||
|
return clock
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Clock $clockId not found; using default")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createClock(DEFAULT_CLOCK_ID)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.keyguard.clock
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.plugins.Clock
|
||||||
|
import com.android.systemui.plugins.ClockId
|
||||||
|
import com.android.systemui.plugins.ClockMetadata
|
||||||
|
import com.android.systemui.plugins.ClockProviderPlugin
|
||||||
|
import com.android.systemui.plugins.PluginListener
|
||||||
|
import com.android.systemui.shared.plugins.PluginManager
|
||||||
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
|
import com.android.systemui.util.mockito.eq
|
||||||
|
import junit.framework.Assert.assertEquals
|
||||||
|
import junit.framework.Assert.fail
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.junit.MockitoJUnit
|
||||||
|
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@SmallTest
|
||||||
|
class ClockRegistryTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@JvmField @Rule val mockito = MockitoJUnit.rule()
|
||||||
|
@Mock private lateinit var mockContext: Context
|
||||||
|
@Mock private lateinit var mockPluginManager: PluginManager
|
||||||
|
@Mock private lateinit var mockClock: Clock
|
||||||
|
@Mock private lateinit var mockThumbnail: Drawable
|
||||||
|
private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
|
||||||
|
private lateinit var registry: ClockRegistry
|
||||||
|
|
||||||
|
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 {
|
||||||
|
private val metadata = mutableListOf<ClockMetadata>()
|
||||||
|
private val createCallbacks = mutableMapOf<ClockId, () -> Clock>()
|
||||||
|
private val thumbnailCallbacks = mutableMapOf<ClockId, () -> Drawable?>()
|
||||||
|
|
||||||
|
override fun getClocks() = metadata
|
||||||
|
override fun createClock(id: ClockId): Clock = createCallbacks[id]!!()
|
||||||
|
override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!()
|
||||||
|
|
||||||
|
fun addClock(
|
||||||
|
id: ClockId,
|
||||||
|
name: String,
|
||||||
|
create: () -> Clock,
|
||||||
|
getThumbnail: () -> Drawable?
|
||||||
|
) {
|
||||||
|
metadata.add(ClockMetadata(id, name))
|
||||||
|
createCallbacks[id] = create
|
||||||
|
thumbnailCallbacks[id] = getThumbnail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
val captor = argumentCaptor<PluginListener<ClockProviderPlugin>>()
|
||||||
|
registry = ClockRegistry(mockContext, mockPluginManager)
|
||||||
|
verify(mockPluginManager).addPluginListener(captor.capture(),
|
||||||
|
eq(ClockProviderPlugin::class.java))
|
||||||
|
pluginListener = captor.value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun pluginRegistration_CorrectState() {
|
||||||
|
val plugin1 = FakeClockPlugin()
|
||||||
|
plugin1.addClock("clock_1", "clock 1", ::failFactory, ::failThumbnail)
|
||||||
|
plugin1.addClock("clock_2", "clock 2", ::failFactory, ::failThumbnail)
|
||||||
|
|
||||||
|
val plugin2 = FakeClockPlugin()
|
||||||
|
plugin2.addClock("clock_3", "clock 3", ::failFactory, ::failThumbnail)
|
||||||
|
plugin2.addClock("clock_4", "clock 4", ::failFactory, ::failThumbnail)
|
||||||
|
|
||||||
|
pluginListener.onPluginConnected(plugin1, mockContext)
|
||||||
|
pluginListener.onPluginConnected(plugin2, mockContext)
|
||||||
|
val list = registry.getClocks()
|
||||||
|
assertEquals(list, listOf(
|
||||||
|
ClockMetadata("clock_1", "clock 1"),
|
||||||
|
ClockMetadata("clock_2", "clock 2"),
|
||||||
|
ClockMetadata("clock_3", "clock 3"),
|
||||||
|
ClockMetadata("clock_4", "clock 4")
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clockIdConflict_ErrorWithoutCrash() {
|
||||||
|
val plugin1 = FakeClockPlugin()
|
||||||
|
plugin1.addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail })
|
||||||
|
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)
|
||||||
|
|
||||||
|
pluginListener.onPluginConnected(plugin1, mockContext)
|
||||||
|
pluginListener.onPluginConnected(plugin2, mockContext)
|
||||||
|
val list = registry.getClocks()
|
||||||
|
assertEquals(list, listOf(
|
||||||
|
ClockMetadata("clock_1", "clock 1"),
|
||||||
|
ClockMetadata("clock_2", "clock 2")
|
||||||
|
))
|
||||||
|
|
||||||
|
assertEquals(registry.createExampleClock("clock_1"), mockClock)
|
||||||
|
assertEquals(registry.createExampleClock("clock_2"), mockClock)
|
||||||
|
assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail)
|
||||||
|
assertEquals(registry.getClockThumbnail("clock_2"), mockThumbnail)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user