Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ad03f4e authored by Robert Snoeberger's avatar Robert Snoeberger
Browse files

Show installed clock plugins in picker app.

Added methods to ClockPlugin to provide information to picker app.
Due to this change, I bumped the api number of the ClockPlugin
interface.

Bug: 125370285
Test: atest ClockManagerTest
Test: checked clock from plugin apk appears in picker app
Change-Id: I0dc3d5a450f7d352900b077b28c670ebadb874e7
parent 00257519
Loading
Loading
Loading
Loading
+29 −7
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
 */
package com.android.systemui.plugins;

import android.graphics.Bitmap;
import android.graphics.Paint.Style;
import android.view.View;

@@ -21,13 +22,30 @@ import com.android.systemui.plugins.annotations.ProvidesInterface;
import java.util.TimeZone;

/**
 * This plugin is used to replace main clock in keyguard.
 * Plugin used to replace main clock in keyguard.
 */
@ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION)
public interface ClockPlugin extends Plugin {

    String ACTION = "com.android.systemui.action.PLUGIN_CLOCK";
    int VERSION = 1;
    int VERSION = 2;

    /**
     * Get the name of the clock face.
     *
     * This name should not be translated.
     */
    String getName();

    /**
     * Get the title of the clock face to be shown in the picker app.
     */
    String getTitle();

    /**
     * Get thumbnail of clock face to be shown in the picker app.
     */
    Bitmap getThumbnail();

    /**
     * Get clock view.
@@ -61,19 +79,23 @@ public interface ClockPlugin extends Plugin {
     */
    default void setColorPalette(boolean supportsDarkText, int[] colors) {}

    /**
     * Notifies that time tick alarm from doze service fired.
     */
    default void dozeTimeTick() {}

    /**
     * Set the amount (ratio) that the device has transitioned to doze.
     * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
     */
    default void setDarkAmount(float darkAmount) {}

    /**
     * Notifies that time tick alarm from doze service fired.
     *
     * Implement this method instead of registering a broadcast listener for TIME_TICK.
     */
    default void onTimeTick() {}

    /**
     * Notifies that the time zone has changed.
     *
     * Implement this method instead of registering a broadcast listener for TIME_ZONE_CHANGED.
     */
    default void onTimeZoneChanged(TimeZone timeZone) {}

+1 −1
Original line number Diff line number Diff line
@@ -279,7 +279,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
     */
    public void dozeTimeTick() {
        if (mClockPlugin != null) {
            mClockPlugin.dozeTimeTick();
            mClockPlugin.onTimeTick();
        }
    }

+44 −13
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@
 */
package com.android.keyguard.clock;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Paint.Style;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,6 +33,16 @@ import java.util.TimeZone;
 */
public class BubbleClockController implements ClockPlugin {

    /**
     * Resources used to get title and thumbnail.
     */
    private final Resources mResources;

    /**
     * LayoutInflater used to inflate custom clock views.
     */
    private final LayoutInflater mLayoutInflater;

    /**
     * Custom clock shown on AOD screen and behind stack scroller on lock.
     */
@@ -48,38 +61,56 @@ public class BubbleClockController implements ClockPlugin {
     */
    private CrossFadeDarkController mDarkController;

    private BubbleClockController() { }

    /**
     * Create a BubbleClockController instance.
     *
     * @param layoutInflater Inflater used to inflate custom clock views.
     */
    public static BubbleClockController build(LayoutInflater layoutInflater) {
        BubbleClockController controller = new BubbleClockController();
        controller.createViews(layoutInflater);
        return controller;
    public BubbleClockController(Resources res, LayoutInflater inflater) {
        mResources = res;
        mLayoutInflater = inflater;
    }

    private void createViews(LayoutInflater layoutInflater) {
        mView = layoutInflater.inflate(R.layout.bubble_clock, null);
    private void createViews() {
        mView = mLayoutInflater.inflate(R.layout.bubble_clock, null);
        mDigitalClock = (TextClock) mView.findViewById(R.id.digital_clock);
        mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock);

        mLockClockContainer = layoutInflater.inflate(R.layout.digital_clock, null);
        mLockClockContainer = mLayoutInflater.inflate(R.layout.digital_clock, null);
        mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock);
        mLockClock.setVisibility(View.GONE);

        mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock);
    }

    @Override
    public String getName() {
        return "bubble";
    }

    @Override
    public String getTitle() {
        return mResources.getString(R.string.clock_title_bubble);
    }

    @Override
    public Bitmap getThumbnail() {
        return BitmapFactory.decodeResource(mResources, R.drawable.bubble_thumbnail);
    }

    @Override
    public View getView() {
        if (mLockClockContainer == null) {
            createViews();
        }
        return mLockClockContainer;
    }

    @Override
    public View getBigClockView() {
        if (mView == null) {
            createViews();
        }
        return mView;
    }

@@ -103,13 +134,13 @@ public class BubbleClockController implements ClockPlugin {
    }

    @Override
    public void dozeTimeTick() {
        mAnalogClock.onTimeChanged();
    public void setDarkAmount(float darkAmount) {
        mDarkController.setDarkAmount(darkAmount);
    }

    @Override
    public void setDarkAmount(float darkAmount) {
        mDarkController.setDarkAmount(darkAmount);
    public void onTimeTick() {
        mAnalogClock.onTimeChanged();
    }

    @Override
+113 −86
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Handler;
@@ -31,6 +30,7 @@ import android.os.Looper;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.MeasureSpec;
@@ -39,17 +39,19 @@ import android.view.ViewGroup;
import androidx.annotation.VisibleForTesting;

import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManager.DockEventListener;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.InjectionInflationController;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

import javax.inject.Inject;
import javax.inject.Singleton;
@@ -67,23 +69,42 @@ public final class ClockManager {
    /**
     * Map from expected value stored in settings to supplier of custom clock face.
     */
    private final Map<String, Supplier<ClockPlugin>> mClocks = new ArrayMap<>();
    private final Map<String, ClockPlugin> mClocks = new ArrayMap<>();
    @Nullable private ClockPlugin mCurrentClock;

    private final LayoutInflater mLayoutInflater;
    private final ContentResolver mContentResolver;
    private final SettingsWrapper mSettingsWrapper;
    private final Handler mMainHandler = new Handler(Looper.getMainLooper());

    /**
     * Observe settings changes to know when to switch the clock face.
     */
    private final ContentObserver mContentObserver =
            new ContentObserver(new Handler(Looper.getMainLooper())) {
            new ContentObserver(mMainHandler) {
                @Override
                public void onChange(boolean selfChange) {
                    super.onChange(selfChange);
                    reload();
                }
            };

    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;

    /**
     * Observe changes to dock state to know when to switch the clock face.
     */
@@ -111,59 +132,29 @@ public final class ClockManager {

    @Inject
    public ClockManager(Context context, InjectionInflationController injectionInflater,
            @Nullable DockManager dockManager, SysuiColorExtractor colorExtractor) {
        this(context, injectionInflater, dockManager, colorExtractor, context.getContentResolver(),
                new SettingsWrapper(context.getContentResolver()));
            PluginManager pluginManager, @Nullable DockManager dockManager,
            SysuiColorExtractor colorExtractor) {
        this(context, injectionInflater, pluginManager, dockManager, colorExtractor,
                context.getContentResolver(), new SettingsWrapper(context.getContentResolver()));
    }

    ClockManager(Context context, InjectionInflationController injectionInflater,
            @Nullable DockManager dockManager, SysuiColorExtractor colorExtractor,
            ContentResolver contentResolver, SettingsWrapper settingsWrapper) {
            PluginManager pluginManager, @Nullable DockManager dockManager,
            SysuiColorExtractor colorExtractor, ContentResolver contentResolver,
            SettingsWrapper settingsWrapper) {
        mPluginManager = pluginManager;
        mDockManager = dockManager;
        mColorExtractor = colorExtractor;
        mContentResolver = contentResolver;
        mSettingsWrapper = settingsWrapper;

        Resources res = context.getResources();
        mClockInfos.add(ClockInfo.builder()
                .setName(DEFAULT_CLOCK_ID)
                .setTitle(res.getString(R.string.clock_title_default))
                .setId(DEFAULT_CLOCK_ID)
                .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.default_thumbnail))
                .setPreview(() -> getClockPreview(DEFAULT_CLOCK_ID))
                .build());
        mClockInfos.add(ClockInfo.builder()
                .setName("bubble")
                .setTitle(res.getString(R.string.clock_title_bubble))
                .setId(BubbleClockController.class.getName())
                .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_thumbnail))
                .setPreview(() -> getClockPreview(BubbleClockController.class.getName()))
                .build());
        mClockInfos.add(ClockInfo.builder()
                .setName("stretch")
                .setTitle(res.getString(R.string.clock_title_stretch))
                .setId(StretchAnalogClockController.class.getName())
                .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_thumbnail))
                .setPreview(() -> getClockPreview(StretchAnalogClockController.class.getName()))
                .build());
        mClockInfos.add(ClockInfo.builder()
                .setName("type")
                .setTitle(res.getString(R.string.clock_title_type))
                .setId(TypeClockController.class.getName())
                .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail))
                .setPreview(() -> getClockPreview(TypeClockController.class.getName()))
                .build());

        LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context));
        mClocks.put(DEFAULT_CLOCK_ID,
                () -> DefaultClockController.build(layoutInflater));
        mClocks.put(BubbleClockController.class.getName(),
                () -> BubbleClockController.build(layoutInflater));
        mClocks.put(StretchAnalogClockController.class.getName(),
                () -> StretchAnalogClockController.build(layoutInflater));
        mClocks.put(TypeClockController.class.getName(),
                () -> TypeClockController.build(layoutInflater));
        mLayoutInflater = layoutInflater;

        addClockPlugin(new DefaultClockController(res, layoutInflater));
        addClockPlugin(new BubbleClockController(res, layoutInflater));
        addClockPlugin(new StretchAnalogClockController(res, layoutInflater));
        addClockPlugin(new TypeClockController(res, layoutInflater));

        // Store the size of the display for generation of clock preview.
        DisplayMetrics dm = res.getDisplayMetrics();
@@ -218,6 +209,29 @@ 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(() -> getClockPreview(id))
                .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;
            }
        }
    }

    /**
     * Generate a realistic preview of a clock face.
     * @param clockId ID of clock to use for preview, should be obtained from {@link getClockInfos}.
@@ -225,12 +239,14 @@ public final class ClockManager {
     */
    @Nullable
    private Bitmap getClockPreview(String clockId) {
        FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() {
            @Override
            public Bitmap call() {
                Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888);
        Supplier<ClockPlugin> supplier = mClocks.get(clockId);
        if (supplier == null) {
                ClockPlugin plugin = mClocks.get(clockId);
                if (plugin == null) {
                    return null;
                }
        ClockPlugin plugin = supplier.get();

                // Use the big clock view for the preview
                View clockView = plugin.getBigClockView();
@@ -242,10 +258,10 @@ public final class ClockManager {
                plugin.setDarkAmount(1f);
                plugin.setTextColor(Color.WHITE);

        ColorExtractor.GradientColors colors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
                true);
                ColorExtractor.GradientColors colors = mColorExtractor.getColors(
                        WallpaperManager.FLAG_LOCK, true);
                plugin.setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
        plugin.dozeTimeTick();
                plugin.onTimeTick();

                // Draw clock view hierarchy to canvas.
                Canvas canvas = new Canvas(bitmap);
@@ -257,6 +273,21 @@ public final class ClockManager {
                clockView.draw(canvas);
                return bitmap;
            }
        });

        if (Looper.myLooper() == Looper.getMainLooper()) {
            task.run();
        } else {
            mMainHandler.post(task);
        }

        try {
            return task.get();
        } catch (Exception e) {
            Log.e(TAG, "Error completing task", e);
            return null;
        }
    }

    private void dispatchVisibilityAggregated(View view, boolean isVisible) {
        // Similar to View.dispatchVisibilityAggregated implementation.
@@ -285,6 +316,7 @@ public final class ClockManager {
    }

    private void register() {
        mPluginManager.addPluginListener(mClockPluginListener, ClockPlugin.class, true);
        mContentResolver.registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
                false, mContentObserver);
@@ -297,6 +329,7 @@ public final class ClockManager {
    }

    private void unregister() {
        mPluginManager.removePluginListener(mClockPluginListener);
        mContentResolver.unregisterContentObserver(mContentObserver);
        if (mDockManager != null) {
            mDockManager.removeListener(mDockEventListener);
@@ -317,21 +350,15 @@ public final class ClockManager {
        if (mIsDocked) {
            final String name = mSettingsWrapper.getDockedClockFace();
            if (name != null) {
                Supplier<ClockPlugin> supplier = mClocks.get(name);
                if (supplier != null) {
                    plugin = supplier.get();
                plugin = mClocks.get(name);
                if (plugin != null) {
                    return plugin;
                }
            }
        }
        }
        final String name = mSettingsWrapper.getLockScreenCustomClockFace();
        if (name != null) {
            Supplier<ClockPlugin> supplier = mClocks.get(name);
            if (supplier != null) {
                plugin = supplier.get();
            }
            plugin = mClocks.get(name);
        }
        return plugin;
    }
+42 −9
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@
 */
package com.android.keyguard.clock;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Paint.Style;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,35 +33,62 @@ import java.util.TimeZone;
 */
public class DefaultClockController implements ClockPlugin {

    /**
     * Resources used to get title and thumbnail.
     */
    private final Resources mResources;

    /**
     * LayoutInflater used to inflate custom clock views.
     */
    private final LayoutInflater mLayoutInflater;

    /**
     * Root view of preview.
     */
    private View mView;

    /**
     * Text clock in preview view hierarchy.
     */
    private TextView mTextTime;
    private TextView mTextDate;

    private DefaultClockController() {}
    /**
     * Date showing below time in preview view hierarchy.
     */
    private TextView mTextDate;

    /**
     * Create a DefaultClockController instance.
     *
     * @param inflater Inflater used to inflate custom clock views.
     */
    public static DefaultClockController build(LayoutInflater inflater) {
        DefaultClockController controller = new DefaultClockController();
        controller.createViews(inflater);
        return controller;
    public DefaultClockController(Resources res, LayoutInflater inflater) {
        mResources = res;
        mLayoutInflater = inflater;
    }

    private void createViews(LayoutInflater inflater) {
        mView = inflater.inflate(R.layout.default_clock_preview, null);
    private void createViews() {
        mView = mLayoutInflater.inflate(R.layout.default_clock_preview, null);
        mTextTime = mView.findViewById(R.id.time);
        mTextDate = mView.findViewById(R.id.date);
    }

    @Override
    public String getName() {
        return "default";
    }

    @Override
    public String getTitle() {
        return mResources.getString(R.string.clock_title_default);
    }

    @Override
    public Bitmap getThumbnail() {
        return BitmapFactory.decodeResource(mResources, R.drawable.default_thumbnail);
    }

    @Override
    public View getView() {
        return null;
@@ -66,6 +96,9 @@ public class DefaultClockController implements ClockPlugin {

    @Override
    public View getBigClockView() {
        if (mView == null) {
            createViews();
        }
        return mView;
    }

@@ -82,7 +115,7 @@ public class DefaultClockController implements ClockPlugin {
    public void setColorPalette(boolean supportsDarkText, int[] colorPalette) {}

    @Override
    public void dozeTimeTick() {
    public void onTimeTick() {
    }

    @Override
Loading