Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | package com.android.keyguard.clock; |
| 17 | |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 18 | import android.annotation.Nullable; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 19 | import android.app.WallpaperManager; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 20 | import android.content.ContentResolver; |
| 21 | import android.content.Context; |
Robert Snoeberger | 6b244b0 | 2019-02-04 15:33:31 -0500 | [diff] [blame] | 22 | import android.content.res.Resources; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 23 | import android.database.ContentObserver; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 24 | import android.graphics.Bitmap; |
| 25 | import android.graphics.Bitmap.Config; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 26 | import android.graphics.Canvas; |
| 27 | import android.graphics.Color; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 28 | import android.os.Handler; |
| 29 | import android.os.Looper; |
| 30 | import android.provider.Settings; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 31 | import android.util.ArrayMap; |
| 32 | import android.util.DisplayMetrics; |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 33 | import android.util.Log; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 34 | import android.view.LayoutInflater; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 35 | import android.view.View; |
| 36 | import android.view.View.MeasureSpec; |
Robert Snoeberger | 60cbc96 | 2019-02-20 15:46:43 -0500 | [diff] [blame] | 37 | import android.view.ViewGroup; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 38 | |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 39 | import androidx.annotation.VisibleForTesting; |
| 40 | |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 41 | import com.android.internal.colorextraction.ColorExtractor; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 42 | import com.android.systemui.colorextraction.SysuiColorExtractor; |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 43 | import com.android.systemui.dock.DockManager; |
| 44 | import com.android.systemui.dock.DockManager.DockEventListener; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 45 | import com.android.systemui.plugins.ClockPlugin; |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 46 | import com.android.systemui.plugins.PluginListener; |
| 47 | import com.android.systemui.shared.plugins.PluginManager; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 48 | import com.android.systemui.util.InjectionInflationController; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 49 | |
| 50 | import java.util.ArrayList; |
| 51 | import java.util.List; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 52 | import java.util.Map; |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 53 | import java.util.concurrent.Callable; |
| 54 | import java.util.concurrent.FutureTask; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 55 | |
| 56 | import javax.inject.Inject; |
| 57 | import javax.inject.Singleton; |
| 58 | |
| 59 | /** |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 60 | * Manages custom clock faces for AOD and lock screen. |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 61 | */ |
| 62 | @Singleton |
| 63 | public final class ClockManager { |
| 64 | |
Robert Snoeberger | 0025751 | 2019-02-27 16:44:04 -0500 | [diff] [blame] | 65 | private static final String TAG = "ClockOptsProvider"; |
| 66 | private static final String DEFAULT_CLOCK_ID = "default"; |
| 67 | |
Robert Snoeberger | 6b244b0 | 2019-02-04 15:33:31 -0500 | [diff] [blame] | 68 | private final List<ClockInfo> mClockInfos = new ArrayList<>(); |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 69 | /** |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 70 | * Map from expected value stored in settings to supplier of custom clock face. |
| 71 | */ |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 72 | private final Map<String, ClockPlugin> mClocks = new ArrayMap<>(); |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 73 | @Nullable private ClockPlugin mCurrentClock; |
| 74 | |
| 75 | private final ContentResolver mContentResolver; |
| 76 | private final SettingsWrapper mSettingsWrapper; |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 77 | private final Handler mMainHandler = new Handler(Looper.getMainLooper()); |
| 78 | |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 79 | /** |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 80 | * Observe settings changes to know when to switch the clock face. |
| 81 | */ |
| 82 | private final ContentObserver mContentObserver = |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 83 | new ContentObserver(mMainHandler) { |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 84 | @Override |
| 85 | public void onChange(boolean selfChange) { |
| 86 | super.onChange(selfChange); |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 87 | reload(); |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 88 | } |
| 89 | }; |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 90 | |
| 91 | private final PluginListener<ClockPlugin> mClockPluginListener = |
| 92 | new PluginListener<ClockPlugin>() { |
| 93 | @Override |
| 94 | public void onPluginConnected(ClockPlugin plugin, Context pluginContext) { |
| 95 | addClockPlugin(plugin); |
| 96 | reload(); |
| 97 | } |
| 98 | |
| 99 | @Override |
| 100 | public void onPluginDisconnected(ClockPlugin plugin) { |
| 101 | removeClockPlugin(plugin); |
| 102 | reload(); |
| 103 | } |
| 104 | }; |
| 105 | |
| 106 | private final PluginManager mPluginManager; |
| 107 | |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 108 | /** |
| 109 | * Observe changes to dock state to know when to switch the clock face. |
| 110 | */ |
| 111 | private final DockEventListener mDockEventListener = |
| 112 | new DockEventListener() { |
| 113 | @Override |
| 114 | public void onEvent(int event) { |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 115 | mIsDocked = (event == DockManager.STATE_DOCKED |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 116 | || event == DockManager.STATE_DOCKED_HIDE); |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 117 | reload(); |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 118 | } |
| 119 | }; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 120 | @Nullable private final DockManager mDockManager; |
| 121 | /** |
| 122 | * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face |
| 123 | * to show. |
| 124 | */ |
| 125 | private boolean mIsDocked; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 126 | |
| 127 | private final List<ClockChangedListener> mListeners = new ArrayList<>(); |
| 128 | |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 129 | private final SysuiColorExtractor mColorExtractor; |
| 130 | private final int mWidth; |
| 131 | private final int mHeight; |
| 132 | |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 133 | @Inject |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 134 | public ClockManager(Context context, InjectionInflationController injectionInflater, |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 135 | PluginManager pluginManager, @Nullable DockManager dockManager, |
| 136 | SysuiColorExtractor colorExtractor) { |
| 137 | this(context, injectionInflater, pluginManager, dockManager, colorExtractor, |
| 138 | context.getContentResolver(), new SettingsWrapper(context.getContentResolver())); |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | ClockManager(Context context, InjectionInflationController injectionInflater, |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 142 | PluginManager pluginManager, @Nullable DockManager dockManager, |
| 143 | SysuiColorExtractor colorExtractor, ContentResolver contentResolver, |
| 144 | SettingsWrapper settingsWrapper) { |
| 145 | mPluginManager = pluginManager; |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 146 | mDockManager = dockManager; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 147 | mColorExtractor = colorExtractor; |
| 148 | mContentResolver = contentResolver; |
| 149 | mSettingsWrapper = settingsWrapper; |
Robert Snoeberger | 6b244b0 | 2019-02-04 15:33:31 -0500 | [diff] [blame] | 150 | |
| 151 | Resources res = context.getResources(); |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 152 | LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context)); |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 153 | |
| 154 | addClockPlugin(new DefaultClockController(res, layoutInflater)); |
| 155 | addClockPlugin(new BubbleClockController(res, layoutInflater)); |
| 156 | addClockPlugin(new StretchAnalogClockController(res, layoutInflater)); |
| 157 | addClockPlugin(new TypeClockController(res, layoutInflater)); |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 158 | |
| 159 | // Store the size of the display for generation of clock preview. |
| 160 | DisplayMetrics dm = res.getDisplayMetrics(); |
| 161 | mWidth = dm.widthPixels; |
| 162 | mHeight = dm.heightPixels; |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | /** |
| 166 | * Add listener to be notified when clock implementation should change. |
| 167 | */ |
| 168 | public void addOnClockChangedListener(ClockChangedListener listener) { |
| 169 | if (mListeners.isEmpty()) { |
| 170 | register(); |
| 171 | } |
| 172 | mListeners.add(listener); |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 173 | reload(); |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 174 | } |
| 175 | |
| 176 | /** |
| 177 | * Remove listener added with {@link addOnClockChangedListener}. |
| 178 | */ |
| 179 | public void removeOnClockChangedListener(ClockChangedListener listener) { |
| 180 | mListeners.remove(listener); |
| 181 | if (mListeners.isEmpty()) { |
| 182 | unregister(); |
| 183 | } |
| 184 | } |
| 185 | |
Robert Snoeberger | 6b244b0 | 2019-02-04 15:33:31 -0500 | [diff] [blame] | 186 | /** |
| 187 | * Get information about available clock faces. |
| 188 | */ |
| 189 | List<ClockInfo> getClockInfos() { |
| 190 | return mClockInfos; |
| 191 | } |
| 192 | |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 193 | /** |
| 194 | * Get the current clock. |
| 195 | * @returns current custom clock or null for default. |
| 196 | */ |
| 197 | @Nullable |
| 198 | ClockPlugin getCurrentClock() { |
| 199 | return mCurrentClock; |
| 200 | } |
| 201 | |
| 202 | @VisibleForTesting |
| 203 | boolean isDocked() { |
| 204 | return mIsDocked; |
| 205 | } |
| 206 | |
| 207 | @VisibleForTesting |
| 208 | ContentObserver getContentObserver() { |
| 209 | return mContentObserver; |
| 210 | } |
| 211 | |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 212 | private void addClockPlugin(ClockPlugin plugin) { |
| 213 | final String id = plugin.getClass().getName(); |
| 214 | mClocks.put(plugin.getClass().getName(), plugin); |
| 215 | mClockInfos.add(ClockInfo.builder() |
| 216 | .setName(plugin.getName()) |
| 217 | .setTitle(plugin.getTitle()) |
| 218 | .setId(id) |
| 219 | .setThumbnail(() -> plugin.getThumbnail()) |
| 220 | .setPreview(() -> getClockPreview(id)) |
| 221 | .build()); |
| 222 | } |
| 223 | |
| 224 | private void removeClockPlugin(ClockPlugin plugin) { |
| 225 | final String id = plugin.getClass().getName(); |
| 226 | mClocks.remove(id); |
| 227 | for (int i = 0; i < mClockInfos.size(); i++) { |
| 228 | if (id.equals(mClockInfos.get(i).getId())) { |
| 229 | mClockInfos.remove(i); |
| 230 | break; |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 235 | /** |
| 236 | * Generate a realistic preview of a clock face. |
| 237 | * @param clockId ID of clock to use for preview, should be obtained from {@link getClockInfos}. |
| 238 | * Returns null if clockId is not found. |
| 239 | */ |
| 240 | @Nullable |
| 241 | private Bitmap getClockPreview(String clockId) { |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 242 | FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() { |
| 243 | @Override |
| 244 | public Bitmap call() { |
| 245 | Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888); |
| 246 | ClockPlugin plugin = mClocks.get(clockId); |
| 247 | if (plugin == null) { |
| 248 | return null; |
| 249 | } |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 250 | |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 251 | // Use the big clock view for the preview |
| 252 | View clockView = plugin.getBigClockView(); |
| 253 | if (clockView == null) { |
| 254 | return null; |
| 255 | } |
| 256 | |
| 257 | // Initialize state of plugin before generating preview. |
| 258 | plugin.setDarkAmount(1f); |
| 259 | plugin.setTextColor(Color.WHITE); |
| 260 | |
| 261 | ColorExtractor.GradientColors colors = mColorExtractor.getColors( |
| 262 | WallpaperManager.FLAG_LOCK, true); |
| 263 | plugin.setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); |
| 264 | plugin.onTimeTick(); |
| 265 | |
| 266 | // Draw clock view hierarchy to canvas. |
| 267 | Canvas canvas = new Canvas(bitmap); |
| 268 | canvas.drawColor(Color.BLACK); |
| 269 | dispatchVisibilityAggregated(clockView, true); |
| 270 | clockView.measure(MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY), |
| 271 | MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY)); |
| 272 | clockView.layout(0, 0, mWidth, mHeight); |
| 273 | clockView.draw(canvas); |
| 274 | return bitmap; |
| 275 | } |
| 276 | }); |
| 277 | |
| 278 | if (Looper.myLooper() == Looper.getMainLooper()) { |
| 279 | task.run(); |
| 280 | } else { |
| 281 | mMainHandler.post(task); |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 282 | } |
| 283 | |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 284 | try { |
| 285 | return task.get(); |
| 286 | } catch (Exception e) { |
| 287 | Log.e(TAG, "Error completing task", e); |
| 288 | return null; |
| 289 | } |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 290 | } |
| 291 | |
Robert Snoeberger | 60cbc96 | 2019-02-20 15:46:43 -0500 | [diff] [blame] | 292 | private void dispatchVisibilityAggregated(View view, boolean isVisible) { |
| 293 | // Similar to View.dispatchVisibilityAggregated implementation. |
| 294 | final boolean thisVisible = view.getVisibility() == View.VISIBLE; |
| 295 | if (thisVisible || !isVisible) { |
| 296 | view.onVisibilityAggregated(isVisible); |
| 297 | } |
| 298 | |
| 299 | if (view instanceof ViewGroup) { |
| 300 | isVisible = thisVisible && isVisible; |
| 301 | ViewGroup vg = (ViewGroup) view; |
| 302 | int count = vg.getChildCount(); |
| 303 | |
| 304 | for (int i = 0; i < count; i++) { |
| 305 | dispatchVisibilityAggregated(vg.getChildAt(i), isVisible); |
| 306 | } |
| 307 | } |
| 308 | } |
| 309 | |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 310 | private void notifyClockChanged(ClockPlugin plugin) { |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 311 | for (int i = 0; i < mListeners.size(); i++) { |
| 312 | // It probably doesn't make sense to supply the same plugin instances to multiple |
| 313 | // listeners. This should be fine for now since there is only a single listener. |
| 314 | mListeners.get(i).onClockChanged(plugin); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | private void register() { |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 319 | mPluginManager.addPluginListener(mClockPluginListener, ClockPlugin.class, true); |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 320 | mContentResolver.registerContentObserver( |
| 321 | Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), |
| 322 | false, mContentObserver); |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 323 | mContentResolver.registerContentObserver( |
| 324 | Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), |
| 325 | false, mContentObserver); |
| 326 | if (mDockManager != null) { |
| 327 | mDockManager.addListener(mDockEventListener); |
| 328 | } |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 329 | } |
| 330 | |
| 331 | private void unregister() { |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 332 | mPluginManager.removePluginListener(mClockPluginListener); |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 333 | mContentResolver.unregisterContentObserver(mContentObserver); |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 334 | if (mDockManager != null) { |
| 335 | mDockManager.removeListener(mDockEventListener); |
| 336 | } |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 337 | } |
| 338 | |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 339 | private void reload() { |
| 340 | mCurrentClock = getClockPlugin(); |
Robert Snoeberger | 0025751 | 2019-02-27 16:44:04 -0500 | [diff] [blame] | 341 | if (mCurrentClock instanceof DefaultClockController) { |
| 342 | notifyClockChanged(null); |
| 343 | } else { |
| 344 | notifyClockChanged(mCurrentClock); |
| 345 | } |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 346 | } |
| 347 | |
| 348 | private ClockPlugin getClockPlugin() { |
| 349 | ClockPlugin plugin = null; |
| 350 | if (mIsDocked) { |
| 351 | final String name = mSettingsWrapper.getDockedClockFace(); |
| 352 | if (name != null) { |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 353 | plugin = mClocks.get(name); |
| 354 | if (plugin != null) { |
| 355 | return plugin; |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 356 | } |
| 357 | } |
| 358 | } |
| 359 | final String name = mSettingsWrapper.getLockScreenCustomClockFace(); |
| 360 | if (name != null) { |
Robert Snoeberger | 9ad03f4 | 2019-02-28 14:47:49 -0500 | [diff] [blame] | 361 | plugin = mClocks.get(name); |
Robert Snoeberger | 71e5079 | 2019-02-15 15:48:01 -0500 | [diff] [blame] | 362 | } |
| 363 | return plugin; |
Robert Snoeberger | b300a4e | 2019-02-13 20:13:53 +0000 | [diff] [blame] | 364 | } |
| 365 | |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 366 | /** |
| 367 | * Listener for events that should cause the custom clock face to change. |
| 368 | */ |
| 369 | public interface ClockChangedListener { |
| 370 | /** |
| 371 | * Called when custom clock should change. |
| 372 | * |
| 373 | * @param clock Custom clock face to use. A null value indicates the default clock face. |
| 374 | */ |
| 375 | void onClockChanged(ClockPlugin clock); |
| 376 | } |
Robert Snoeberger | 15b4af1 | 2019-01-18 15:37:27 -0500 | [diff] [blame] | 377 | } |