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

Commit ea1fb1e0 authored by Lucas Dupin's avatar Lucas Dupin
Browse files

Wallpaper color extraction

Now it's possible to listen to changes on wallpaper colors by
registering a listener on WallpaperManager. It's also possible
to know the current wallpaper colors and if it's light or dark.

Test: runtest --path cts/tests/app/src/android/app/cts/WallpaperColorsTest.java && \
      runtest --path cts/tests/app/src/android/app/cts/WallpaperManagerTest.java
Bug: 36856508
Change-Id: Ia6b317b710e721d26f0fe41c847b9426e61d8d8b
parent 799ba76a
Loading
Loading
Loading
Loading
+20 −0
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.os.ParcelFileDescriptor;
import android.app.IWallpaperManagerCallback;
import android.app.IWallpaperManagerCallback;
import android.app.WallpaperInfo;
import android.app.WallpaperInfo;
import android.content.ComponentName;
import android.content.ComponentName;
import android.app.WallpaperColors;


/** @hide */
/** @hide */
interface IWallpaperManager {
interface IWallpaperManager {
@@ -135,4 +136,23 @@ interface IWallpaperManager {
     * wallpaper content has changed.
     * wallpaper content has changed.
     */
     */
    boolean setLockWallpaperCallback(IWallpaperManagerCallback cb);
    boolean setLockWallpaperCallback(IWallpaperManagerCallback cb);

    /**
     * Returns the colors used by the lock screen or system wallpaper.
     *
     * @param which either {@link WallpaperManager#FLAG_LOCK}
     * or {@link WallpaperManager#FLAG_SYSTEM}
     * @return colors of chosen wallpaper
     */
    WallpaperColors getWallpaperColors(int which);

    /**
     * Register a callback to receive color updates
     */
    void registerWallpaperColorsCallback(IWallpaperManagerCallback cb);

    /**
     * Unregister a callback that was receiving color updates
     */
    void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb);
}
}
+8 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package android.app;
package android.app;


import android.app.WallpaperColors;

/**
/**
 * Callback interface used by IWallpaperManager to send asynchronous 
 * Callback interface used by IWallpaperManager to send asynchronous 
 * notifications back to its clients.  Note that this is a
 * notifications back to its clients.  Note that this is a
@@ -28,4 +30,10 @@ oneway interface IWallpaperManagerCallback {
     * Called when the wallpaper has changed
     * Called when the wallpaper has changed
     */
     */
    void onWallpaperChanged();
    void onWallpaperChanged();

    /**
     * Called when wallpaper colors change
     */
    void onWallpaperColorsChanged(in WallpaperColors colors, int which);

}
}
+19 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 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 android.app;

parcelable WallpaperColors;
 No newline at end of file
+60 −2
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.os.Parcelable;


import android.util.Pair;
import android.util.Pair;


import java.util.ArrayList;
import java.util.List;
import java.util.List;


/**
/**
@@ -29,7 +30,19 @@ import java.util.List;
 */
 */
public final class WallpaperColors implements Parcelable {
public final class WallpaperColors implements Parcelable {


    private static final float BRIGHT_LUMINANCE = 0.9f;
    private final List<Pair<Color, Integer>> mColors;
    private final boolean mSupportsDarkText;

    public WallpaperColors(Parcel parcel) {
    public WallpaperColors(Parcel parcel) {
        mColors = new ArrayList<>();
        int count = parcel.readInt();
        for (int i=0; i < count; i++) {
            Color color = Color.valueOf(parcel.readInt());
            int weight = parcel.readInt();
            mColors.add(new Pair<>(color, weight));
        }
        mSupportsDarkText = parcel.readBoolean();
    }
    }


    /**
    /**
@@ -43,6 +56,7 @@ public final class WallpaperColors implements Parcelable {
     *               and number of occurrences/influence.
     *               and number of occurrences/influence.
     */
     */
    public WallpaperColors(List<Pair<Color, Integer>> colors) {
    public WallpaperColors(List<Pair<Color, Integer>> colors) {
        this(colors, calculateDarkTextSupport(colors));
    }
    }


    /**
    /**
@@ -55,6 +69,10 @@ public final class WallpaperColors implements Parcelable {
     * @param supportsDarkText can have dark text on top or not
     * @param supportsDarkText can have dark text on top or not
     */
     */
    public WallpaperColors(List<Pair<Color, Integer>> colors, boolean supportsDarkText) {
    public WallpaperColors(List<Pair<Color, Integer>> colors, boolean supportsDarkText) {
        if (colors == null)
            colors = new ArrayList<>();
        mColors = colors;
        mSupportsDarkText = supportsDarkText;
    }
    }


    public static final Creator<WallpaperColors> CREATOR = new Creator<WallpaperColors>() {
    public static final Creator<WallpaperColors> CREATOR = new Creator<WallpaperColors>() {
@@ -76,6 +94,13 @@ public final class WallpaperColors implements Parcelable {


    @Override
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    public void writeToParcel(Parcel dest, int flags) {
        int count = mColors.size();
        dest.writeInt(count);
        for (Pair<Color, Integer> color : mColors) {
            dest.writeInt(color.first.toArgb());
            dest.writeInt(color.second);
        }
        dest.writeBoolean(mSupportsDarkText);
    }
    }


    /**
    /**
@@ -83,7 +108,22 @@ public final class WallpaperColors implements Parcelable {
     * @return list of colors paired with their weights.
     * @return list of colors paired with their weights.
     */
     */
    public List<Pair<Color, Integer>> getColors() {
    public List<Pair<Color, Integer>> getColors() {
        return null;
        return mColors;
    }

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        WallpaperColors other = (WallpaperColors) o;
        return mColors.equals(other.mColors) && mSupportsDarkText == other.mSupportsDarkText;
    }

    @Override
    public int hashCode() {
        return 31 * mColors.hashCode() + (mSupportsDarkText ? 1 : 0);
    }
    }


    /**
    /**
@@ -92,6 +132,24 @@ public final class WallpaperColors implements Parcelable {
     * @return true if dark text is supported
     * @return true if dark text is supported
     */
     */
    public boolean supportsDarkText() {
    public boolean supportsDarkText() {
        return mSupportsDarkText;
    }

    private static boolean calculateDarkTextSupport(List<Pair<Color, Integer>> colors) {
        if (colors == null) {
            return false;
            return false;
        }
        }

        Pair<Color, Integer> mainColor = null;

        for (Pair<Color, Integer> color : colors) {
            if (mainColor == null) {
                mainColor = color;
            } else if (color.second > mainColor.second) {
                mainColor = color;
            }
        }
        return mainColor != null &&
                mainColor.first.luminance() > BRIGHT_LUMINANCE;
    }
}
}
+103 −6
Original line number Original line Diff line number Diff line
@@ -52,13 +52,13 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.os.Looper;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Log;
import android.util.Log;
import android.util.Pair;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerGlobal;


import libcore.io.IoUtils;
import libcore.io.IoUtils;
@@ -71,6 +71,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;
@@ -271,15 +273,20 @@ public class WallpaperManager {
        }
        }
    }
    }


    static class Globals extends IWallpaperManagerCallback.Stub {
    private static class Globals extends IWallpaperManagerCallback.Stub {
        private final IWallpaperManager mService;
        private final IWallpaperManager mService;
        private boolean mColorCallbackRegistered;
        private final ArrayList<Pair<OnColorsChangedListener, Handler>> mColorListeners =
                new ArrayList<>();
        private Bitmap mCachedWallpaper;
        private Bitmap mCachedWallpaper;
        private int mCachedWallpaperUserId;
        private int mCachedWallpaperUserId;
        private Bitmap mDefaultWallpaper;
        private Bitmap mDefaultWallpaper;
        private Handler mMainLooperHandler;


        Globals(Looper looper) {
        Globals(Looper looper) {
            IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
            IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
            mService = IWallpaperManager.Stub.asInterface(b);
            mService = IWallpaperManager.Stub.asInterface(b);
            mMainLooperHandler = new Handler(looper);
            forgetLoadedWallpaper();
            forgetLoadedWallpaper();
        }
        }


@@ -292,6 +299,88 @@ public class WallpaperManager {
            forgetLoadedWallpaper();
            forgetLoadedWallpaper();
        }
        }


        /**
         * Start listening to wallpaper color events.
         * Will be called whenever someone changes their wallpaper or if a live wallpaper
         * changes its colors.
         * @param callback Listener
         * @param handler Thread to call it from. Main thread if null.
         */
        public void addOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
                @Nullable Handler handler) {
            synchronized (this) {
                if (!mColorCallbackRegistered) {
                    try {
                        mService.registerWallpaperColorsCallback(this);
                        mColorCallbackRegistered = true;
                    } catch (RemoteException e) {
                        // Failed, service is gone
                        Log.w(TAG, "Can't register for color updates", e);
                    }
                }
                mColorListeners.add(new Pair<>(callback, handler));
            }
        }

        /**
         * Stop listening to wallpaper color events.
         *
         * @param callback listener
         */
        public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback) {
            synchronized (this) {
                mColorListeners.removeIf(pair -> pair.first == callback);

                if (mColorListeners.size() == 0 && mColorCallbackRegistered) {
                    mColorCallbackRegistered = false;
                    try {
                        mService.unregisterWallpaperColorsCallback(this);
                    } catch (RemoteException e) {
                        // Failed, service is gone
                        Log.w(TAG, "Can't unregister color updates", e);
                    }
                }
            }
        }

        @Override
        public void onWallpaperColorsChanged(WallpaperColors colors, int which) {
            synchronized (this) {
                for (Pair<OnColorsChangedListener, Handler> listener : mColorListeners) {
                    Handler handler = listener.second;
                    if (listener.second == null) {
                        handler = mMainLooperHandler;
                    }
                    handler.post(() -> {
                        // Dealing with race conditions between posting a callback and
                        // removeOnColorsChangedListener being called.
                        boolean stillExists;
                        synchronized (sGlobals) {
                            stillExists = mColorListeners.contains(listener);
                        }
                        if (stillExists) {
                            listener.first.onColorsChanged(colors, which);
                        }
                    });
                }
            }
        }

        WallpaperColors getWallpaperColors(int which) {
            synchronized (this) {
                if (which != FLAG_LOCK && which != FLAG_SYSTEM)
                    throw new IllegalArgumentException(
                            "which should be either FLAG_LOCK or FLAG_SYSTEM");

                try {
                    return mService.getWallpaperColors(which);
                } catch (RemoteException e) {
                    // Can't get colors, connection lost.
                }
                return null;
            }
        }

        public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
        public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
                @SetWallpaperFlags int which) {
                @SetWallpaperFlags int which) {
            return peekWallpaperBitmap(context, returnDefault, which, context.getUserId());
            return peekWallpaperBitmap(context, returnDefault, which, context.getUserId());
@@ -746,7 +835,6 @@ public class WallpaperManager {
        return getWallpaperFile(which, mContext.getUserId());
        return getWallpaperFile(which, mContext.getUserId());
    }
    }



    /**
    /**
     * Registers a listener to get notified when the wallpaper colors change.
     * Registers a listener to get notified when the wallpaper colors change.
     * Callback might be called from an arbitrary background thread.
     * Callback might be called from an arbitrary background thread.
@@ -754,16 +842,18 @@ public class WallpaperManager {
     * @param listener A listener to register
     * @param listener A listener to register
     */
     */
    public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
    public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
        sGlobals.addOnColorsChangedListener(listener, null);
    }
    }


    /**
    /**
     * Registers a listener to get notified when the wallpaper colors change
     * Registers a listener to get notified when the wallpaper colors change
     * @param listener A listener to register
     * @param listener A listener to register
     * @param handler Where to call it from. Might be called from a background thread
     * @param handler Where to call it from. Will be called from the main thread
     *                if null.
     *                if null.
     */
     */
    public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
    public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
            @Nullable Handler handler) {
            @NonNull Handler handler) {
        sGlobals.addOnColorsChangedListener(listener, handler);
    }
    }


    /**
    /**
@@ -771,6 +861,7 @@ public class WallpaperManager {
     * @param callback A callback to unsubscribe
     * @param callback A callback to unsubscribe
     */
     */
    public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback) {
    public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback) {
        sGlobals.removeOnColorsChangedListener(callback);
    }
    }


    /**
    /**
@@ -780,7 +871,7 @@ public class WallpaperManager {
     * @return a list of colors ordered by priority
     * @return a list of colors ordered by priority
     */
     */
    public @Nullable WallpaperColors getWallpaperColors(int which) {
    public @Nullable WallpaperColors getWallpaperColors(int which) {
        return null;
        return sGlobals.getWallpaperColors(which);
    }
    }


    /**
    /**
@@ -1773,6 +1864,12 @@ public class WallpaperManager {
        public void onWallpaperChanged() throws RemoteException {
        public void onWallpaperChanged() throws RemoteException {
            mLatch.countDown();
            mLatch.countDown();
        }
        }

        @Override
        public void onWallpaperColorsChanged(WallpaperColors colors, int which)
            throws RemoteException {
            sGlobals.onWallpaperColorsChanged(colors, which);
        }
    }
    }


    /**
    /**
Loading