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

Commit 44426e21 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Switch clock face while docked."

parents a057c94b 0ac3cdfa
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -6103,7 +6103,7 @@ public final class Settings {
         * Indicates which clock face to show on lock screen and AOD while docked.
         * @hide
         */
        private static final String DOCKED_CLOCK_FACE = "docked_clock_face";
        public static final String DOCKED_CLOCK_FACE = "docked_clock_face";
        /**
         * Set by the system to track if the user needs to see the call to action for
+1 −0
Original line number Diff line number Diff line
@@ -620,6 +620,7 @@ public class SettingsBackupTest {
                 Settings.Secure.DISABLED_PRINT_SERVICES,
                 Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
                 Settings.Secure.DISPLAY_DENSITY_FORCED,
                 Settings.Secure.DOCKED_CLOCK_FACE,
                 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                 Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
                 Settings.Secure.ENABLED_INPUT_METHODS,  // Intentionally removed in P
+47 −70
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.keyguard.clock;

import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -25,16 +26,18 @@ import android.os.Looper;
import android.provider.Settings;
import android.view.LayoutInflater;

import androidx.annotation.VisibleForTesting;

import com.android.keyguard.R;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManager.DockEventListener;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.inject.Inject;
import javax.inject.Singleton;
@@ -45,7 +48,6 @@ import javax.inject.Singleton;
@Singleton
public final class ClockManager {

    private final LayoutInflater mLayoutInflater;
    private final ContentResolver mContentResolver;

    private final List<ClockInfo> mClockInfos = new ArrayList<>();
@@ -62,7 +64,6 @@ public final class ClockManager {
                    }
                }
            };

    private final ExtensionController mExtensionController;
    /**
     * Used to select between plugin or default implementations of ClockPlugin interface.
@@ -72,13 +73,35 @@ public final class ClockManager {
     * Consumer that accepts the a new ClockPlugin implementation when the Extension reloads.
     */
    private final Consumer<ClockPlugin> mClockPluginConsumer = this::setClockPlugin;
    /**
     * Supplier of default ClockPlugin implementation.
     */
    private final DefaultClockSupplier mDefaultClockSupplier;
    /**
     * Observe changes to dock state to know when to switch the clock face.
     */
    private final DockEventListener mDockEventListener =
            new DockEventListener() {
                @Override
                public void onEvent(int event) {
                    final boolean isDocked = (event == DockManager.STATE_DOCKED
                            || event == DockManager.STATE_DOCKED_HIDE);
                    mDefaultClockSupplier.setDocked(isDocked);
                    if (mClockExtension != null) {
                        mClockExtension.reload();
                    }
                }
            };
    @Nullable
    private final DockManager mDockManager;

    private final List<ClockChangedListener> mListeners = new ArrayList<>();

    @Inject
    public ClockManager(Context context, ExtensionController extensionController) {
    public ClockManager(Context context, ExtensionController extensionController,
            @Nullable DockManager dockManager) {
        mExtensionController = extensionController;
        mLayoutInflater = LayoutInflater.from(context);
        mDockManager = dockManager;
        mContentResolver = context.getContentResolver();

        Resources res = context.getResources();
@@ -110,6 +133,9 @@ public final class ClockManager {
                .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail))
                .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview))
                .build());

        mDefaultClockSupplier = new DefaultClockSupplier(new SettingsWrapper(mContentResolver),
                LayoutInflater.from(context));
    }

    /**
@@ -154,41 +180,32 @@ public final class ClockManager {
        mContentResolver.registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
                false, mContentObserver);
        mContentResolver.registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE),
                false, mContentObserver);
        if (mDockManager != null) {
            mDockManager.addListener(mDockEventListener);
        }
        mClockExtension = mExtensionController.newExtension(ClockPlugin.class)
            .withPlugin(ClockPlugin.class)
            .withCallback(mClockPluginConsumer)
            // Using withDefault even though this isn't the default as a workaround.
            // ExtensionBuilder doesn't provide the ability to supply a ClockPlugin
            // instance based off of the value of a setting. Since multiple "default"
            // can be provided, using a supplier that changes the settings value.
            // A null return will cause Extension#reload to look at the next "default"
            // supplier.
            .withDefault(
                    new SettingsGattedSupplier(
                        mContentResolver,
                        Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
                        BubbleClockController.class.getName(),
                            () -> BubbleClockController.build(mLayoutInflater)))
            .withDefault(
                    new SettingsGattedSupplier(
                        mContentResolver,
                        Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
                        StretchAnalogClockController.class.getName(),
                            () -> StretchAnalogClockController.build(mLayoutInflater)))
            .withDefault(
                    new SettingsGattedSupplier(
                        mContentResolver,
                        Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
                        TypeClockController.class.getName(),
                            () -> TypeClockController.build(mLayoutInflater)))
            .withDefault(mDefaultClockSupplier)
            .build();
    }

    private void unregister() {
        mContentResolver.unregisterContentObserver(mContentObserver);
        if (mDockManager != null) {
            mDockManager.removeListener(mDockEventListener);
        }
        mClockExtension.destroy();
    }

    @VisibleForTesting
    boolean isDocked() {
        return mDefaultClockSupplier.isDocked();
    }

    /**
     * Listener for events that should cause the custom clock face to change.
     */
@@ -200,44 +217,4 @@ public final class ClockManager {
         */
        void onClockChanged(ClockPlugin clock);
    }

    /**
     * Supplier that only gets an instance when a settings value matches expected value.
     */
    private static class SettingsGattedSupplier implements Supplier<ClockPlugin> {

        private final ContentResolver mContentResolver;
        private final String mKey;
        private final String mValue;
        private final Supplier<ClockPlugin> mSupplier;

        /**
         * Constructs a supplier that changes secure setting key against value.
         *
         * @param contentResolver Used to look up settings value.
         * @param key Settings key.
         * @param value If the setting matches this values that get supplies a ClockPlugin
         *        instance.
         * @param supplier Supplier of ClockPlugin instance, only used if the setting
         *        matches value.
         */
        SettingsGattedSupplier(ContentResolver contentResolver, String key, String value,
                Supplier<ClockPlugin> supplier) {
            mContentResolver = contentResolver;
            mKey = key;
            mValue = value;
            mSupplier = supplier;
        }

        /**
         * Returns null if the settings value doesn't match the expected value.
         *
         * A null return causes Extension#reload to skip this supplier and move to the next.
         */
        @Override
        public ClockPlugin get() {
            final String currentValue = Settings.Secure.getString(mContentResolver, mKey);
            return Objects.equals(currentValue, mValue) ? mSupplier.get() : null;
        }
    }
}
+101 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.util.ArrayMap;
import android.view.LayoutInflater;

import com.android.systemui.plugins.ClockPlugin;

import java.util.Map;
import java.util.function.Supplier;

/**
 * Supplier that only gets an instance when a settings value matches expected value.
 */
public class DefaultClockSupplier implements Supplier<ClockPlugin> {

    private final SettingsWrapper mSettingsWrapper;
    /**
     * Map from expected value stored in settings to supplier of custom clock face.
     */
    private final Map<String, Supplier<ClockPlugin>> mClocks = new ArrayMap<>();
    /**
     * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face
     * to show.
     */
    private boolean mIsDocked;

    /**
     * Constructs a supplier that changes secure setting key against value.
     *
     * @param settingsWrapper Wrapper around settings used to look up the custom clock face.
     * @param layoutInflater Provided to clocks as dependency to inflate clock views.
     */
    public DefaultClockSupplier(SettingsWrapper settingsWrapper, LayoutInflater layoutInflater) {
        mSettingsWrapper = settingsWrapper;

        mClocks.put(BubbleClockController.class.getName(),
                () -> BubbleClockController.build(layoutInflater));
        mClocks.put(StretchAnalogClockController.class.getName(),
                () -> StretchAnalogClockController.build(layoutInflater));
        mClocks.put(TypeClockController.class.getName(),
                () -> TypeClockController.build(layoutInflater));
    }

    /**
     * Sets the dock state.
     *
     * @param isDocked True when docked, false otherwise.
     */
    public void setDocked(boolean isDocked) {
        mIsDocked = isDocked;
    }

    boolean isDocked() {
        return mIsDocked;
    }

    /**
     * Get the custom clock face based on values in settings.
     *
     * @return Custom clock face, null if the settings value doesn't match a custom clock.
     */
    @Override
    public ClockPlugin get() {
        ClockPlugin plugin = null;
        if (mIsDocked) {
            final String name = mSettingsWrapper.getDockedClockFace();
            if (name != null) {
                Supplier<ClockPlugin> supplier = mClocks.get(name);
                if (supplier != null) {
                    plugin = supplier.get();
                    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();
            }
        }
        return plugin;
    }
}
+48 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.ContentResolver;
import android.provider.Settings;

/**
 * Wrapper around Settings used for testing.
 */
public class SettingsWrapper {

    private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE;
    private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE;

    private ContentResolver mContentResolver;

    public SettingsWrapper(ContentResolver contentResolver) {
        mContentResolver = contentResolver;
    }

    /**
     * Gets the value stored in settings for the custom clock face.
     */
    public String getLockScreenCustomClockFace() {
        return Settings.Secure.getString(mContentResolver, CUSTOM_CLOCK_FACE);
    }

    /**
     * Gets the value stored in settings for the clock face to use when docked.
     */
    public String getDockedClockFace() {
        return Settings.Secure.getString(mContentResolver, DOCKED_CLOCK_FACE);
    }
}
Loading