Loading packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +147 −86 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import com.android.systemui.util.InjectionInflationController; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Supplier; import javax.inject.Inject; import javax.inject.Singleton; Loading @@ -52,14 +53,9 @@ import javax.inject.Singleton; public final class ClockManager { private static final String TAG = "ClockOptsProvider"; private static final String DEFAULT_CLOCK_ID = "default"; private final List<ClockInfo> mClockInfos = new ArrayList<>(); /** * Map from expected value stored in settings to supplier of custom clock face. */ private final Map<String, ClockPlugin> mClocks = new ArrayMap<>(); @Nullable private ClockPlugin mCurrentClock; private final AvailableClocks mPreviewClocks; private final List<Supplier<ClockPlugin>> mBuiltinClocks = new ArrayList<>(); private final Context mContext; private final ContentResolver mContentResolver; Loading @@ -78,21 +74,6 @@ public final class ClockManager { } }; private final PluginListener<ClockPlugin> mClockPluginListener = new PluginListener<ClockPlugin>() { @Override public void onPluginConnected(ClockPlugin plugin, Context pluginContext) { addClockPlugin(plugin); reload(); } @Override public void onPluginDisconnected(ClockPlugin plugin) { removeClockPlugin(plugin); reload(); } }; private final PluginManager mPluginManager; /** Loading @@ -108,13 +89,22 @@ public final class ClockManager { } }; @Nullable private DockManager mDockManager; /** * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face * to show. */ private boolean mIsDocked; private final List<ClockChangedListener> mListeners = new ArrayList<>(); /** * Listeners for onClockChanged event. * * Each listener must receive a separate clock plugin instance. Otherwise, there could be * problems like attempting to attach a view that already has a parent. To deal with this issue, * each listener is associated with a collection of available clocks. When onClockChanged is * fired the current clock plugin instance is retrieved from that listeners available clocks. */ private final Map<ClockChangedListener, AvailableClocks> mListeners = new ArrayMap<>(); private final int mWidth; private final int mHeight; Loading @@ -133,14 +123,16 @@ public final class ClockManager { mPluginManager = pluginManager; mContentResolver = contentResolver; mSettingsWrapper = settingsWrapper; mPreviewClocks = new AvailableClocks(); Resources res = context.getResources(); LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context)); addClockPlugin(new DefaultClockController(res, layoutInflater, colorExtractor)); addClockPlugin(new BubbleClockController(res, layoutInflater, colorExtractor)); addClockPlugin(new StretchAnalogClockController(res, layoutInflater, colorExtractor)); addClockPlugin(new TypeClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new StretchAnalogClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new TypeClockController(res, layoutInflater, colorExtractor)); // Store the size of the display for generation of clock preview. DisplayMetrics dm = res.getDisplayMetrics(); Loading @@ -155,7 +147,12 @@ public final class ClockManager { if (mListeners.isEmpty()) { register(); } mListeners.add(listener); AvailableClocks availableClocks = new AvailableClocks(); for (int i = 0; i < mBuiltinClocks.size(); i++) { availableClocks.addClockPlugin(mBuiltinClocks.get(i).get()); } mListeners.put(listener, availableClocks); mPluginManager.addPluginListener(availableClocks, ClockPlugin.class, true); reload(); } Loading @@ -163,7 +160,8 @@ public final class ClockManager { * Remove listener added with {@link addOnClockChangedListener}. */ public void removeOnClockChangedListener(ClockChangedListener listener) { mListeners.remove(listener); AvailableClocks availableClocks = mListeners.remove(listener); mPluginManager.removePluginListener(availableClocks); if (mListeners.isEmpty()) { unregister(); } Loading @@ -173,16 +171,16 @@ public final class ClockManager { * Get information about available clock faces. */ List<ClockInfo> getClockInfos() { return mClockInfos; return mPreviewClocks.getInfo(); } /** * Get the current clock. * @returns current custom clock or null for default. * @return current custom clock or null for default. */ @Nullable ClockPlugin getCurrentClock() { return mCurrentClock; return mPreviewClocks.getCurrentClock(); } @VisibleForTesting Loading @@ -195,39 +193,14 @@ public final class ClockManager { return mContentObserver; } private void addClockPlugin(ClockPlugin plugin) { final String id = plugin.getClass().getName(); mClocks.put(plugin.getClass().getName(), plugin); mClockInfos.add(ClockInfo.builder() .setName(plugin.getName()) .setTitle(plugin.getTitle()) .setId(id) .setThumbnail(() -> plugin.getThumbnail()) .setPreview(() -> plugin.getPreview(mWidth, mHeight)) .build()); } private void removeClockPlugin(ClockPlugin plugin) { final String id = plugin.getClass().getName(); mClocks.remove(id); for (int i = 0; i < mClockInfos.size(); i++) { if (id.equals(mClockInfos.get(i).getId())) { mClockInfos.remove(i); break; } } } private void notifyClockChanged(ClockPlugin plugin) { for (int i = 0; i < mListeners.size(); i++) { // It probably doesn't make sense to supply the same plugin instances to multiple // listeners. This should be fine for now since there is only a single listener. mListeners.get(i).onClockChanged(plugin); } private void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) { ClockPlugin plugin = pluginSupplier.get(); mPreviewClocks.addClockPlugin(plugin); mBuiltinClocks.add(pluginSupplier); } private void register() { mPluginManager.addPluginListener(mClockPluginListener, ClockPlugin.class, true); mPluginManager.addPluginListener(mPreviewClocks, ClockPlugin.class, true); mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), false, mContentObserver); Loading @@ -243,7 +216,7 @@ public final class ClockManager { } private void unregister() { mPluginManager.removePluginListener(mClockPluginListener); mPluginManager.removePluginListener(mPreviewClocks); mContentResolver.unregisterContentObserver(mContentObserver); if (mDockManager != null) { mDockManager.removeListener(mDockEventListener); Loading @@ -251,17 +224,116 @@ public final class ClockManager { } private void reload() { mCurrentClock = getClockPlugin(); if (mCurrentClock instanceof DefaultClockController) { notifyClockChanged(null); mPreviewClocks.reload(); mListeners.forEach((listener, clocks) -> { clocks.reload(); ClockPlugin clock = clocks.getCurrentClock(); if (clock instanceof DefaultClockController) { listener.onClockChanged(null); } else { notifyClockChanged(mCurrentClock); listener.onClockChanged(clock); } }); } /** * Listener for events that should cause the custom clock face to change. */ public interface ClockChangedListener { /** * Called when custom clock should change. * * @param clock Custom clock face to use. A null value indicates the default clock face. */ void onClockChanged(ClockPlugin clock); } /** * Collection of available clocks. */ private final class AvailableClocks implements PluginListener<ClockPlugin> { /** * Map from expected value stored in settings to plugin for custom clock face. */ private final Map<String, ClockPlugin> mClocks = new ArrayMap<>(); /** * Metadata about available clocks, such as name and preview images. */ private final List<ClockInfo> mClockInfo = new ArrayList<>(); /** * Active ClockPlugin. */ @Nullable private ClockPlugin mCurrentClock; @Override public void onPluginConnected(ClockPlugin plugin, Context pluginContext) { addClockPlugin(plugin); reload(); } @Override public void onPluginDisconnected(ClockPlugin plugin) { removeClockPlugin(plugin); reload(); } /** * Get the current clock. * @return current custom clock or null for default. */ @Nullable ClockPlugin getCurrentClock() { return mCurrentClock; } /** * Get information about available clock faces. */ List<ClockInfo> getInfo() { return mClockInfo; } /** * Adds a clock plugin to the collection of available clocks. * * @param plugin The plugin to add. */ void addClockPlugin(ClockPlugin plugin) { final String id = plugin.getClass().getName(); mClocks.put(plugin.getClass().getName(), plugin); mClockInfo.add(ClockInfo.builder() .setName(plugin.getName()) .setTitle(plugin.getTitle()) .setId(id) .setThumbnail(plugin::getThumbnail) .setPreview(() -> plugin.getPreview(mWidth, mHeight)) .build()); } private void removeClockPlugin(ClockPlugin plugin) { final String id = plugin.getClass().getName(); mClocks.remove(id); for (int i = 0; i < mClockInfo.size(); i++) { if (id.equals(mClockInfo.get(i).getId())) { mClockInfo.remove(i); break; } } } /** * Update the current clock. */ void reload() { mCurrentClock = getClockPlugin(); } private ClockPlugin getClockPlugin() { ClockPlugin plugin = null; if (mIsDocked) { if (ClockManager.this.isDocked()) { final String name = mSettingsWrapper.getDockedClockFace(); if (name != null) { plugin = mClocks.get(name); Loading @@ -276,16 +348,5 @@ public final class ClockManager { } return plugin; } /** * Listener for events that should cause the custom clock face to change. */ public interface ClockChangedListener { /** * Called when custom clock should change. * * @param clock Custom clock face to use. A null value indicates the default clock face. */ void onClockChanged(ClockPlugin clock); } } packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +40 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.keyguard.clock; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ContentResolver; Loading @@ -31,6 +33,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerFake; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.InjectionInflationController; Loading @@ -38,6 +41,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; Loading @@ -57,7 +61,8 @@ public final class ClockManagerTest extends SysuiTestCase { @Mock SysuiColorExtractor mMockColorExtractor; @Mock ContentResolver mMockContentResolver; @Mock SettingsWrapper mMockSettingsWrapper; @Mock ClockManager.ClockChangedListener mMockListener; @Mock ClockManager.ClockChangedListener mMockListener1; @Mock ClockManager.ClockChangedListener mMockListener2; @Before public void setUp() { Loading @@ -73,13 +78,17 @@ public final class ClockManagerTest extends SysuiTestCase { mMockPluginManager, mMockColorExtractor, mMockContentResolver, mMockSettingsWrapper); mClockManager.addOnClockChangedListener(mMockListener); mClockManager.addOnClockChangedListener(mMockListener1); mClockManager.addOnClockChangedListener(mMockListener2); reset(mMockListener1, mMockListener2); mContentObserver = mClockManager.getContentObserver(); } @After public void tearDown() { mClockManager.removeOnClockChangedListener(mMockListener); mClockManager.removeOnClockChangedListener(mMockListener1); mClockManager.removeOnClockChangedListener(mMockListener2); } @Test Loading Loading @@ -115,6 +124,34 @@ public final class ClockManagerTest extends SysuiTestCase { assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); } @Test public void onClockChanged_customClock() { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired mContentObserver.onChange(false); // THEN the plugin is the bubble clock face. ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class); verify(mMockListener1).onClockChanged(captor.capture()); assertThat(captor.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); } @Test public void onClockChanged_uniqueInstances() { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired mContentObserver.onChange(false); // THEN the listeners receive separate instances of the Bubble clock plugin. ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class); ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class); verify(mMockListener1).onClockChanged(captor1.capture()); verify(mMockListener2).onClockChanged(captor2.capture()); assertThat(captor1.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); assertThat(captor2.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); assertThat(captor1.getValue()).isNotSameAs(captor2.getValue()); } @Test public void getCurrentClock_badSettingsValue() { // GIVEN that settings contains a value that doesn't correspond to a Loading Loading
packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +147 −86 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import com.android.systemui.util.InjectionInflationController; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Supplier; import javax.inject.Inject; import javax.inject.Singleton; Loading @@ -52,14 +53,9 @@ import javax.inject.Singleton; public final class ClockManager { private static final String TAG = "ClockOptsProvider"; private static final String DEFAULT_CLOCK_ID = "default"; private final List<ClockInfo> mClockInfos = new ArrayList<>(); /** * Map from expected value stored in settings to supplier of custom clock face. */ private final Map<String, ClockPlugin> mClocks = new ArrayMap<>(); @Nullable private ClockPlugin mCurrentClock; private final AvailableClocks mPreviewClocks; private final List<Supplier<ClockPlugin>> mBuiltinClocks = new ArrayList<>(); private final Context mContext; private final ContentResolver mContentResolver; Loading @@ -78,21 +74,6 @@ public final class ClockManager { } }; private final PluginListener<ClockPlugin> mClockPluginListener = new PluginListener<ClockPlugin>() { @Override public void onPluginConnected(ClockPlugin plugin, Context pluginContext) { addClockPlugin(plugin); reload(); } @Override public void onPluginDisconnected(ClockPlugin plugin) { removeClockPlugin(plugin); reload(); } }; private final PluginManager mPluginManager; /** Loading @@ -108,13 +89,22 @@ public final class ClockManager { } }; @Nullable private DockManager mDockManager; /** * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face * to show. */ private boolean mIsDocked; private final List<ClockChangedListener> mListeners = new ArrayList<>(); /** * Listeners for onClockChanged event. * * Each listener must receive a separate clock plugin instance. Otherwise, there could be * problems like attempting to attach a view that already has a parent. To deal with this issue, * each listener is associated with a collection of available clocks. When onClockChanged is * fired the current clock plugin instance is retrieved from that listeners available clocks. */ private final Map<ClockChangedListener, AvailableClocks> mListeners = new ArrayMap<>(); private final int mWidth; private final int mHeight; Loading @@ -133,14 +123,16 @@ public final class ClockManager { mPluginManager = pluginManager; mContentResolver = contentResolver; mSettingsWrapper = settingsWrapper; mPreviewClocks = new AvailableClocks(); Resources res = context.getResources(); LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context)); addClockPlugin(new DefaultClockController(res, layoutInflater, colorExtractor)); addClockPlugin(new BubbleClockController(res, layoutInflater, colorExtractor)); addClockPlugin(new StretchAnalogClockController(res, layoutInflater, colorExtractor)); addClockPlugin(new TypeClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new StretchAnalogClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new TypeClockController(res, layoutInflater, colorExtractor)); // Store the size of the display for generation of clock preview. DisplayMetrics dm = res.getDisplayMetrics(); Loading @@ -155,7 +147,12 @@ public final class ClockManager { if (mListeners.isEmpty()) { register(); } mListeners.add(listener); AvailableClocks availableClocks = new AvailableClocks(); for (int i = 0; i < mBuiltinClocks.size(); i++) { availableClocks.addClockPlugin(mBuiltinClocks.get(i).get()); } mListeners.put(listener, availableClocks); mPluginManager.addPluginListener(availableClocks, ClockPlugin.class, true); reload(); } Loading @@ -163,7 +160,8 @@ public final class ClockManager { * Remove listener added with {@link addOnClockChangedListener}. */ public void removeOnClockChangedListener(ClockChangedListener listener) { mListeners.remove(listener); AvailableClocks availableClocks = mListeners.remove(listener); mPluginManager.removePluginListener(availableClocks); if (mListeners.isEmpty()) { unregister(); } Loading @@ -173,16 +171,16 @@ public final class ClockManager { * Get information about available clock faces. */ List<ClockInfo> getClockInfos() { return mClockInfos; return mPreviewClocks.getInfo(); } /** * Get the current clock. * @returns current custom clock or null for default. * @return current custom clock or null for default. */ @Nullable ClockPlugin getCurrentClock() { return mCurrentClock; return mPreviewClocks.getCurrentClock(); } @VisibleForTesting Loading @@ -195,39 +193,14 @@ public final class ClockManager { return mContentObserver; } private void addClockPlugin(ClockPlugin plugin) { final String id = plugin.getClass().getName(); mClocks.put(plugin.getClass().getName(), plugin); mClockInfos.add(ClockInfo.builder() .setName(plugin.getName()) .setTitle(plugin.getTitle()) .setId(id) .setThumbnail(() -> plugin.getThumbnail()) .setPreview(() -> plugin.getPreview(mWidth, mHeight)) .build()); } private void removeClockPlugin(ClockPlugin plugin) { final String id = plugin.getClass().getName(); mClocks.remove(id); for (int i = 0; i < mClockInfos.size(); i++) { if (id.equals(mClockInfos.get(i).getId())) { mClockInfos.remove(i); break; } } } private void notifyClockChanged(ClockPlugin plugin) { for (int i = 0; i < mListeners.size(); i++) { // It probably doesn't make sense to supply the same plugin instances to multiple // listeners. This should be fine for now since there is only a single listener. mListeners.get(i).onClockChanged(plugin); } private void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) { ClockPlugin plugin = pluginSupplier.get(); mPreviewClocks.addClockPlugin(plugin); mBuiltinClocks.add(pluginSupplier); } private void register() { mPluginManager.addPluginListener(mClockPluginListener, ClockPlugin.class, true); mPluginManager.addPluginListener(mPreviewClocks, ClockPlugin.class, true); mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), false, mContentObserver); Loading @@ -243,7 +216,7 @@ public final class ClockManager { } private void unregister() { mPluginManager.removePluginListener(mClockPluginListener); mPluginManager.removePluginListener(mPreviewClocks); mContentResolver.unregisterContentObserver(mContentObserver); if (mDockManager != null) { mDockManager.removeListener(mDockEventListener); Loading @@ -251,17 +224,116 @@ public final class ClockManager { } private void reload() { mCurrentClock = getClockPlugin(); if (mCurrentClock instanceof DefaultClockController) { notifyClockChanged(null); mPreviewClocks.reload(); mListeners.forEach((listener, clocks) -> { clocks.reload(); ClockPlugin clock = clocks.getCurrentClock(); if (clock instanceof DefaultClockController) { listener.onClockChanged(null); } else { notifyClockChanged(mCurrentClock); listener.onClockChanged(clock); } }); } /** * Listener for events that should cause the custom clock face to change. */ public interface ClockChangedListener { /** * Called when custom clock should change. * * @param clock Custom clock face to use. A null value indicates the default clock face. */ void onClockChanged(ClockPlugin clock); } /** * Collection of available clocks. */ private final class AvailableClocks implements PluginListener<ClockPlugin> { /** * Map from expected value stored in settings to plugin for custom clock face. */ private final Map<String, ClockPlugin> mClocks = new ArrayMap<>(); /** * Metadata about available clocks, such as name and preview images. */ private final List<ClockInfo> mClockInfo = new ArrayList<>(); /** * Active ClockPlugin. */ @Nullable private ClockPlugin mCurrentClock; @Override public void onPluginConnected(ClockPlugin plugin, Context pluginContext) { addClockPlugin(plugin); reload(); } @Override public void onPluginDisconnected(ClockPlugin plugin) { removeClockPlugin(plugin); reload(); } /** * Get the current clock. * @return current custom clock or null for default. */ @Nullable ClockPlugin getCurrentClock() { return mCurrentClock; } /** * Get information about available clock faces. */ List<ClockInfo> getInfo() { return mClockInfo; } /** * Adds a clock plugin to the collection of available clocks. * * @param plugin The plugin to add. */ void addClockPlugin(ClockPlugin plugin) { final String id = plugin.getClass().getName(); mClocks.put(plugin.getClass().getName(), plugin); mClockInfo.add(ClockInfo.builder() .setName(plugin.getName()) .setTitle(plugin.getTitle()) .setId(id) .setThumbnail(plugin::getThumbnail) .setPreview(() -> plugin.getPreview(mWidth, mHeight)) .build()); } private void removeClockPlugin(ClockPlugin plugin) { final String id = plugin.getClass().getName(); mClocks.remove(id); for (int i = 0; i < mClockInfo.size(); i++) { if (id.equals(mClockInfo.get(i).getId())) { mClockInfo.remove(i); break; } } } /** * Update the current clock. */ void reload() { mCurrentClock = getClockPlugin(); } private ClockPlugin getClockPlugin() { ClockPlugin plugin = null; if (mIsDocked) { if (ClockManager.this.isDocked()) { final String name = mSettingsWrapper.getDockedClockFace(); if (name != null) { plugin = mClocks.get(name); Loading @@ -276,16 +348,5 @@ public final class ClockManager { } return plugin; } /** * Listener for events that should cause the custom clock face to change. */ public interface ClockChangedListener { /** * Called when custom clock should change. * * @param clock Custom clock face to use. A null value indicates the default clock face. */ void onClockChanged(ClockPlugin clock); } }
packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +40 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.keyguard.clock; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ContentResolver; Loading @@ -31,6 +33,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerFake; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.InjectionInflationController; Loading @@ -38,6 +41,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; Loading @@ -57,7 +61,8 @@ public final class ClockManagerTest extends SysuiTestCase { @Mock SysuiColorExtractor mMockColorExtractor; @Mock ContentResolver mMockContentResolver; @Mock SettingsWrapper mMockSettingsWrapper; @Mock ClockManager.ClockChangedListener mMockListener; @Mock ClockManager.ClockChangedListener mMockListener1; @Mock ClockManager.ClockChangedListener mMockListener2; @Before public void setUp() { Loading @@ -73,13 +78,17 @@ public final class ClockManagerTest extends SysuiTestCase { mMockPluginManager, mMockColorExtractor, mMockContentResolver, mMockSettingsWrapper); mClockManager.addOnClockChangedListener(mMockListener); mClockManager.addOnClockChangedListener(mMockListener1); mClockManager.addOnClockChangedListener(mMockListener2); reset(mMockListener1, mMockListener2); mContentObserver = mClockManager.getContentObserver(); } @After public void tearDown() { mClockManager.removeOnClockChangedListener(mMockListener); mClockManager.removeOnClockChangedListener(mMockListener1); mClockManager.removeOnClockChangedListener(mMockListener2); } @Test Loading Loading @@ -115,6 +124,34 @@ public final class ClockManagerTest extends SysuiTestCase { assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); } @Test public void onClockChanged_customClock() { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired mContentObserver.onChange(false); // THEN the plugin is the bubble clock face. ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class); verify(mMockListener1).onClockChanged(captor.capture()); assertThat(captor.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); } @Test public void onClockChanged_uniqueInstances() { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired mContentObserver.onChange(false); // THEN the listeners receive separate instances of the Bubble clock plugin. ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class); ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class); verify(mMockListener1).onClockChanged(captor1.capture()); verify(mMockListener2).onClockChanged(captor2.capture()); assertThat(captor1.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); assertThat(captor2.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); assertThat(captor1.getValue()).isNotSameAs(captor2.getValue()); } @Test public void getCurrentClock_badSettingsValue() { // GIVEN that settings contains a value that doesn't correspond to a Loading