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

Commit b233058b authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Adding support for listening to icon config changes

Bug: 183641907
Test: Manual
Change-Id: If1dbf346df34865db23c22c43a36ac9b4403bc18
parent 451f13e4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -84,7 +84,7 @@ public class BaseIconFactory implements AutoCloseable {
        clear();
    }

    protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
    public BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
        this(context, fillResIconDpi, iconBitmapSize, false);
    }

+98 −50
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.launcher3.icons;

import static android.content.Intent.ACTION_DATE_CHANGED;
import static android.content.Intent.ACTION_TIMEZONE_CHANGED;
import static android.content.Intent.ACTION_TIME_CHANGED;
import static android.content.res.Resources.ID_NULL;

import android.content.BroadcastReceiver;
@@ -32,6 +35,7 @@ import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.PatternMatcher;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -45,7 +49,8 @@ import com.android.launcher3.util.SafeCloseable;
import org.xmlpull.v1.XmlPullParser;

import java.util.Calendar;
import java.util.function.BiConsumer;
import java.util.Collections;
import java.util.Map;
import java.util.function.Supplier;

/**
@@ -53,6 +58,10 @@ import java.util.function.Supplier;
 */
public class IconProvider {

    private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
    private static final int CONFIG_ICON_MASK_RES_ID = Resources.getSystem().getIdentifier(
            "config_icon_mask", "string", "android");

    private static final String TAG_ICON = "icon";
    private static final String ATTR_PACKAGE = "package";
    private static final String ATTR_DRAWABLE = "drawable";
@@ -65,7 +74,9 @@ public class IconProvider {
    private static final String SYSTEM_STATE_SEPARATOR = " ";
    private static final String THEMED_ICON_MAP_FILE = "grayscale_icon_map";

    private ArrayMap<String, ThemeData> mThemedIconMap;
    private static final Map<String, ThemeData> DISABLED_MAP = Collections.emptyMap();

    private Map<String, ThemeData> mThemedIconMap;

    private final Context mContext;
    private final ComponentName mCalendar;
@@ -81,8 +92,15 @@ public class IconProvider {
        mClock = parseComponentOrNull(context, R.string.clock_component_name);
        if (!supportsIconTheme) {
            // Initialize an empty map if theming is not supported
            mThemedIconMap = new ArrayMap<>();
            mThemedIconMap = DISABLED_MAP;
        }
    }

    /**
     * Enables or disables icon theme support
     */
    public void setIconThemeSupported(boolean isSupported) {
        mThemedIconMap = isSupported ? null : DISABLED_MAP;
    }

    /**
@@ -97,15 +115,6 @@ public class IconProvider {
        }
    }

    /**
     * Loads the icon for the provided ActivityInfo such that it can be drawn directly
     * on the UI
     * @deprecated Use {@link #getIcon}
     */
    public Drawable getIconForUI(ActivityInfo info, UserHandle user) {
        return getIcon(info);
    }

    /**
     * Loads the icon for the provided LauncherActivityInfo
     */
@@ -165,7 +174,7 @@ public class IconProvider {
        return icon;
    }

    private ArrayMap<String, ThemeData> getThemedIconMap() {
    private Map<String, ThemeData> getThemedIconMap() {
        if (mThemedIconMap != null) {
            return mThemedIconMap;
        }
@@ -260,60 +269,99 @@ public class IconProvider {
        return Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1;
    }

    /**
     * Registers a callback to listen for calendar icon changes.
     * The callback receives the packageName for the calendar icon
     */
    public static SafeCloseable registerIconChangeListener(Context context,
            BiConsumer<String, UserHandle> callback, Handler handler) {
        ComponentName calendar = parseComponentOrNull(context, R.string.calendar_component_name);
        ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);

        if (calendar == null && clock == null) {
            return () -> { };
    private static ComponentName parseComponentOrNull(Context context, int resId) {
        String cn = context.getString(resId);
        return TextUtils.isEmpty(cn) ? null : ComponentName.unflattenFromString(cn);
    }

        BroadcastReceiver receiver = new DateTimeChangeReceiver(callback);
        final IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
        if (calendar != null) {
            filter.addAction(Intent.ACTION_TIME_CHANGED);
            filter.addAction(Intent.ACTION_DATE_CHANGED);
    /**
     * Returns a string representation of the current system icon state
     */
    public String getSystemIconState() {
        return (CONFIG_ICON_MASK_RES_ID == ID_NULL
                ? "" : mContext.getResources().getString(CONFIG_ICON_MASK_RES_ID))
                + (mThemedIconMap == DISABLED_MAP ? ",no-theme" : ",with-theme");
    }
        context.registerReceiver(receiver, filter, null, handler);

        return () -> context.unregisterReceiver(receiver);
    /**
     * Registers a callback to listen for various system dependent icon changes.
     */
    public SafeCloseable registerIconChangeListener(IconChangeListener listener, Handler handler) {
        return new IconChangeReceiver(listener, handler);
    }

    private static class DateTimeChangeReceiver extends BroadcastReceiver {
    private class IconChangeReceiver extends BroadcastReceiver implements SafeCloseable {

        private final BiConsumer<String, UserHandle> mCallback;
        private final IconChangeListener mCallback;
        private String mIconState;

        DateTimeChangeReceiver(BiConsumer<String, UserHandle> callback) {
        IconChangeReceiver(IconChangeListener callback, Handler handler) {
            mCallback = callback;
        }
            mIconState = getSystemIconState();

        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
                ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);
                if (clock != null) {
                    mCallback.accept(clock.getPackageName(), Process.myUserHandle());

            IntentFilter packageFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
            packageFilter.addDataScheme("package");
            packageFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
            mContext.registerReceiver(this, packageFilter, null, handler);

            if (mCalendar != null || mClock != null) {
                final IntentFilter filter = new IntentFilter(ACTION_TIMEZONE_CHANGED);
                if (mCalendar != null) {
                    filter.addAction(Intent.ACTION_TIME_CHANGED);
                    filter.addAction(ACTION_DATE_CHANGED);
                }
                mContext.registerReceiver(this, filter, null, handler);
            }
        }

            ComponentName calendar =
                    parseComponentOrNull(context, R.string.calendar_component_name);
            if (calendar != null) {
        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                case ACTION_TIMEZONE_CHANGED:
                    if (mClock != null) {
                        mCallback.onAppIconChanged(mClock.getPackageName(), Process.myUserHandle());
                    }
                    // follow through
                case ACTION_DATE_CHANGED:
                case ACTION_TIME_CHANGED:
                    if (mCalendar != null) {
                        for (UserHandle user
                                : context.getSystemService(UserManager.class).getUserProfiles()) {
                    mCallback.accept(calendar.getPackageName(), user);
                            mCallback.onAppIconChanged(mCalendar.getPackageName(), user);
                        }
                    }
                    break;
                case ACTION_OVERLAY_CHANGED: {
                    String newState = getSystemIconState();
                    if (!mIconState.equals(newState)) {
                        mIconState = newState;
                        mCallback.onSystemIconStateChanged(mIconState);
                    }
                    break;
                }
            }
        }

    private static ComponentName parseComponentOrNull(Context context, int resId) {
        String cn = context.getString(resId);
        return TextUtils.isEmpty(cn) ? null : ComponentName.unflattenFromString(cn);
        @Override
        public void close() {
            mContext.unregisterReceiver(this);
        }
    }

    /**
     * Listener for receiving icon changes
     */
    public interface IconChangeListener {

        /**
         * Called when the icon for a particular app changes
         */
        void onAppIconChanged(String packageName, UserHandle user);

        /**
         * Called when the global icon state changed, which can typically affect all icons
         */
        void onSystemIconStateChanged(String iconState);
    }
}