Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +2 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import java.util.TimeZone; /** * Plugin used to replace main clock in keyguard. * @deprecated Migrating to ClockProviderPlugin */ @Deprecated @ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION) public interface ClockPlugin extends Plugin { Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * 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 ) No newline at end of file packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +3 −0 Original line number Diff line number Diff line Loading @@ -53,8 +53,11 @@ import javax.inject.Inject; /** * Manages custom clock faces for AOD and lock screen. * * @deprecated Migrate to ClockRegistry */ @SysUISingleton @Deprecated public final class ClockManager { private static final String TAG = "ClockOptsProvider"; Loading packages/SystemUI/src/com/android/keyguard/clock/ClockRegistry.kt 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * 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 ) } No newline at end of file packages/SystemUI/tests/src/com/android/keyguard/clock/ClockRegistryTest.kt 0 → 100644 +138 −0 Original line number Diff line number Diff line /* * 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) } } Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +2 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import java.util.TimeZone; /** * Plugin used to replace main clock in keyguard. * @deprecated Migrating to ClockProviderPlugin */ @Deprecated @ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION) public interface ClockPlugin extends Plugin { Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * 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 ) No newline at end of file
packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +3 −0 Original line number Diff line number Diff line Loading @@ -53,8 +53,11 @@ import javax.inject.Inject; /** * Manages custom clock faces for AOD and lock screen. * * @deprecated Migrate to ClockRegistry */ @SysUISingleton @Deprecated public final class ClockManager { private static final String TAG = "ClockOptsProvider"; Loading
packages/SystemUI/src/com/android/keyguard/clock/ClockRegistry.kt 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * 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 ) } No newline at end of file
packages/SystemUI/tests/src/com/android/keyguard/clock/ClockRegistryTest.kt 0 → 100644 +138 −0 Original line number Diff line number Diff line /* * 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) } }