diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 75a5bf7f77a22..411d0e208e54d 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -22,6 +22,7 @@ import android.os.ParcelFileDescriptor; import android.app.IWallpaperManagerCallback; import android.app.WallpaperInfo; import android.content.ComponentName; +import android.app.WallpaperColors; /** @hide */ interface IWallpaperManager { @@ -135,4 +136,23 @@ interface IWallpaperManager { * wallpaper content has changed. */ 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); } diff --git a/core/java/android/app/IWallpaperManagerCallback.aidl b/core/java/android/app/IWallpaperManagerCallback.aidl index 991b2bc924b5f..0cfbaef6d6eb1 100644 --- a/core/java/android/app/IWallpaperManagerCallback.aidl +++ b/core/java/android/app/IWallpaperManagerCallback.aidl @@ -16,6 +16,8 @@ package android.app; +import android.app.WallpaperColors; + /** * Callback interface used by IWallpaperManager to send asynchronous * notifications back to its clients. Note that this is a @@ -28,4 +30,10 @@ oneway interface IWallpaperManagerCallback { * Called when the wallpaper has changed */ void onWallpaperChanged(); + + /** + * Called when wallpaper colors change + */ + void onWallpaperColorsChanged(in WallpaperColors colors, int which); + } diff --git a/core/java/android/app/WallpaperColors.aidl b/core/java/android/app/WallpaperColors.aidl new file mode 100644 index 0000000000000..f5edff271615f --- /dev/null +++ b/core/java/android/app/WallpaperColors.aidl @@ -0,0 +1,19 @@ +/* + * 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 diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index 5ed66cac5f8fe..b3c70a49f660a 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -22,6 +22,7 @@ import android.os.Parcelable; import android.util.Pair; +import java.util.ArrayList; import java.util.List; /** @@ -29,7 +30,19 @@ import java.util.List; */ public final class WallpaperColors implements Parcelable { + private static final float BRIGHT_LUMINANCE = 0.9f; + private final List> mColors; + private final boolean mSupportsDarkText; + 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. */ public WallpaperColors(List> 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 */ public WallpaperColors(List> colors, boolean supportsDarkText) { + if (colors == null) + colors = new ArrayList<>(); + mColors = colors; + mSupportsDarkText = supportsDarkText; } public static final Creator CREATOR = new Creator() { @@ -76,6 +94,13 @@ public final class WallpaperColors implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + int count = mColors.size(); + dest.writeInt(count); + for (Pair 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. */ public List> 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 */ public boolean supportsDarkText() { - return false; + return mSupportsDarkText; + } + + private static boolean calculateDarkTextSupport(List> colors) { + if (colors == null) { + return false; + } + + Pair mainColor = null; + + for (Pair color : colors) { + if (mainColor == null) { + mainColor = color; + } else if (color.second > mainColor.second) { + mainColor = color; + } + } + return mainColor != null && + mainColor.first.luminance() > BRIGHT_LUMINANCE; } } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 6d87de8e30013..8a33791adfdbd 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -52,13 +52,13 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.ParcelFileDescriptor; -import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import android.view.WindowManagerGlobal; import libcore.io.IoUtils; @@ -71,6 +71,8 @@ import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; 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 boolean mColorCallbackRegistered; + private final ArrayList> mColorListeners = + new ArrayList<>(); private Bitmap mCachedWallpaper; private int mCachedWallpaperUserId; private Bitmap mDefaultWallpaper; + private Handler mMainLooperHandler; Globals(Looper looper) { IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE); mService = IWallpaperManager.Stub.asInterface(b); + mMainLooperHandler = new Handler(looper); forgetLoadedWallpaper(); } @@ -292,6 +299,88 @@ public class WallpaperManager { 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 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, @SetWallpaperFlags int which) { return peekWallpaperBitmap(context, returnDefault, which, context.getUserId()); @@ -746,7 +835,6 @@ public class WallpaperManager { return getWallpaperFile(which, mContext.getUserId()); } - /** * Registers a listener to get notified when the wallpaper colors change. * Callback might be called from an arbitrary background thread. @@ -754,16 +842,18 @@ public class WallpaperManager { * @param listener A listener to register */ public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener) { + sGlobals.addOnColorsChangedListener(listener, null); } /** * Registers a listener to get notified when the wallpaper colors change * @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. */ 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 */ public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback) { + sGlobals.removeOnColorsChangedListener(callback); } /** @@ -780,7 +871,7 @@ public class WallpaperManager { * @return a list of colors ordered by priority */ public @Nullable WallpaperColors getWallpaperColors(int which) { - return null; + return sGlobals.getWallpaperColors(which); } /** @@ -1773,6 +1864,12 @@ public class WallpaperManager { public void onWallpaperChanged() throws RemoteException { mLatch.countDown(); } + + @Override + public void onWallpaperColorsChanged(WallpaperColors colors, int which) + throws RemoteException { + sGlobals.onWallpaperColorsChanged(colors, which); + } } /** diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl index f9c5aaa97612f..3c3ef0c0e9eb4 100644 --- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl +++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl @@ -18,6 +18,7 @@ package android.service.wallpaper; import android.os.ParcelFileDescriptor; import android.service.wallpaper.IWallpaperEngine; +import android.app.WallpaperColors; /** * @hide @@ -26,4 +27,5 @@ interface IWallpaperConnection { void attachEngine(IWallpaperEngine engine); void engineShown(IWallpaperEngine engine); ParcelFileDescriptor setWallpaper(String name); + void onWallpaperColorsChanged(in WallpaperColors colors); } diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index de527e958c6b2..eff52e6ca1b5f 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -30,5 +30,5 @@ oneway interface IWallpaperEngine { void dispatchPointer(in MotionEvent event); void dispatchWallpaperCommand(String action, int x, int y, int z, in Bundle extras); - void destroy(); + void destroy(); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 98780a7d73d56..539278fd47f1e 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -550,6 +550,12 @@ public abstract class WallpaperService extends Service { * This will trigger a {@link #onComputeWallpaperColors()} call. */ public void invalidateColors() { + try { + mConnection.onWallpaperColorsChanged(onComputeWallpaperColors()); + } catch (RemoteException e) { + Log.w(TAG, "Can't invalidate wallpaper colors because " + + "wallpaper connection was lost", e); + } } /** @@ -562,7 +568,7 @@ public abstract class WallpaperService extends Service { public @Nullable WallpaperColors onComputeWallpaperColors() { return null; } - + protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { out.print(prefix); out.print("mInitializing="); out.print(mInitializing); out.print(" mDestroyed="); out.println(mDestroyed); @@ -1199,6 +1205,7 @@ public abstract class WallpaperService extends Service { mEngine = engine; mActiveEngines.add(engine); engine.attach(this); + engine.invalidateColors(); return; } case DO_DETACH: { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index 99b3aa878c7b2..c4879011b9f8b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -36,6 +36,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.app.WallpaperColors; import android.util.Log; import com.android.keyguard.KeyguardUpdateMonitor; @@ -156,6 +157,11 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen postUpdateWallpaper(); } + @Override + public void onWallpaperColorsChanged(WallpaperColors colors, int which) { + + } + private void postUpdateWallpaper() { mH.removeCallbacks(this); mH.post(this); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 6a18beb813082..d572003ce9964 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -52,6 +52,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; +import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; @@ -73,10 +74,12 @@ import android.os.UserManager; import android.service.wallpaper.IWallpaperConnection; import android.service.wallpaper.IWallpaperEngine; import android.service.wallpaper.IWallpaperService; +import android.app.WallpaperColors; import android.service.wallpaper.WallpaperService; import android.system.ErrnoException; import android.system.Os; import android.util.EventLog; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.Xml; @@ -86,6 +89,7 @@ import android.view.WindowManager; import com.android.internal.R; import com.android.internal.content.PackageMonitor; +import com.android.internal.graphics.palette.Palette; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; @@ -94,6 +98,7 @@ import com.android.server.EventLogTags; import com.android.server.FgThread; import com.android.server.SystemService; +import java.util.ArrayList; import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; @@ -219,6 +224,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { // lock-only changes happen on the dedicated lock wallpaper input file final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile)); final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile)); + int notifyColorsWhich = 0; WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged); if (DEBUG) { @@ -240,6 +246,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } SELinux.restorecon(changedFile); notifyLockWallpaperChanged(); + notifyWallpaperColorsChanged(wallpaper, FLAG_LOCK); return; } @@ -284,6 +291,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { // If this was the system wallpaper, rebind... bindWallpaperComponentLocked(mImageWallpaper, true, false, wallpaper, null); + notifyColorsWhich |= FLAG_SYSTEM; } if (lockWallpaperChanged || (wallpaper.whichPending & FLAG_LOCK) != 0) { @@ -298,12 +306,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } // and in any case, tell keyguard about it notifyLockWallpaperChanged(); + notifyColorsWhich |= FLAG_LOCK; } + saveSettingsLocked(wallpaper.userId); } } } } + + // Outside of the lock since it will synchronize itself + if (notifyColorsWhich != 0) { + notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich); + } } } @@ -318,6 +333,90 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } } + private void notifyWallpaperColorsChanged(WallpaperData wallpaper, int which) { + boolean needsExtraction; + synchronized (mLock) { + if (mColorsChangedListeners.getRegisteredCallbackCount() == 0) + return; + + if (DEBUG) { + Slog.v(TAG, "notifyWallpaperColorsChanged " + which); + } + + needsExtraction = wallpaper.primaryColors == null; + } + + // This should not be synchronized because color extraction + // might take a while. + if (needsExtraction) { + extractColors(wallpaper); + } + + synchronized (mLock) { + final int n = mColorsChangedListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IWallpaperManagerCallback callback = mColorsChangedListeners.getBroadcastItem(i); + try { + callback.onWallpaperColorsChanged(wallpaper.primaryColors, which); + } catch (RemoteException e) { + // Callback is gone, it's not necessary to unregister it since + // RemoteCallbackList#getBroadcastItem will take care of it. + } + } + mColorsChangedListeners.finishBroadcast(); + + final IWallpaperManagerCallback cb = mKeyguardListener; + if (cb != null) { + try { + cb.onWallpaperColorsChanged(wallpaper.primaryColors, which); + } catch (RemoteException e) { + // Oh well it went away; no big deal + } + } + } + } + + private void extractColors(WallpaperData wallpaper) { + String cropFile = null; + int wallpaperId = -1; + synchronized (mLock) { + // Only extract colors of ImageWallpaper or lock wallpapers (null) + final boolean supportedComponent = mImageWallpaper.equals(wallpaper.wallpaperComponent) + || wallpaper.wallpaperComponent == null; + if (!supportedComponent) + return; + + if (wallpaper.cropFile != null && wallpaper.cropFile.exists()) { + cropFile = wallpaper.cropFile.getAbsolutePath(); + } + wallpaperId = wallpaper.wallpaperId; + } + + if (cropFile != null) { + final Bitmap bitmap = BitmapFactory.decodeFile(cropFile); + if (bitmap == null) { + Slog.w(TAG, "Cannot extract colors because wallpaper file could not be read."); + return; + } + Palette palette = Palette.from(bitmap).generate(); + bitmap.recycle(); + + final List> colors = new ArrayList<>(); + for (Palette.Swatch swatch : palette.getSwatches()) { + colors.add(new Pair<>(Color.valueOf(swatch.getRgb()), + swatch.getPopulation())); + } + + synchronized (mLock) { + if (wallpaper.wallpaperId == wallpaperId) { + wallpaper.primaryColors = new WallpaperColors(colors); + } else { + Slog.w(TAG, "Not setting primary colors since wallpaper changed"); + } + } + } + } + /** * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped * for display. @@ -481,6 +580,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { final IPackageManager mIPackageManager; final MyPackageMonitor mMonitor; final AppOpsManager mAppOpsManager; + final RemoteCallbackList mColorsChangedListeners; WallpaperData mLastWallpaper; IWallpaperManagerCallback mKeyguardListener; boolean mWaitingForUnlock; @@ -557,6 +657,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { */ int wallpaperId; + /** + * Primary colors histogram + */ + WallpaperColors primaryColors; + WallpaperConnection connection; long lastDiedTime; boolean wallpaperUpdating; @@ -706,6 +811,35 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } } + /** + * Called by a live wallpaper if its colors have changed. + * @param primaryColors representation of wallpaper primary colors + */ + @Override + public void onWallpaperColorsChanged(WallpaperColors primaryColors) { + int which; + synchronized (mLock) { + // Do not broadcast changes on ImageWallpaper since it's handled + // internally by this class. + if (mImageWallpaper.equals(mWallpaper.wallpaperComponent)) { + return; + } + + mWallpaper.primaryColors = primaryColors; + + // Live wallpapers always are system wallpapers. + which = FLAG_SYSTEM; + // It's also the lock screen wallpaper when we don't have a bitmap in there + WallpaperData lockedWallpaper = mLockWallpaperMap.get(mCurrentUserId); + if (lockedWallpaper == null) { + which |= FLAG_LOCK; + } + } + if (which != 0) { + notifyWallpaperColorsChanged(mWallpaper, which); + } + } + @Override public void attachEngine(IWallpaperEngine engine) { synchronized (mLock) { @@ -916,6 +1050,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { mMonitor.register(context, null, UserHandle.ALL, true); getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs(); loadSettingsLocked(UserHandle.USER_SYSTEM, false); + mColorsChangedListeners = new RemoteCallbackList<>(); } private static File getWallpaperDir(int userId) { @@ -1130,6 +1265,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { synchronized (mLock) { clearWallpaperLocked(false, which, userId, null); } + + // When clearing a wallpaper, broadcast new valid colors + WallpaperData data = getWallpaperSafeLocked(mCurrentUserId, which); + notifyWallpaperColorsChanged(data, which); } void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) { @@ -1421,6 +1560,20 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { return -1; } + @Override + public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb) { + synchronized (mLock) { + mColorsChangedListeners.register(cb); + } + } + + @Override + public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb) { + synchronized (mLock) { + mColorsChangedListeners.unregister(cb); + } + } + @Override public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) { checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW); @@ -1430,6 +1583,41 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { return true; } + @Override + public WallpaperColors getWallpaperColors(int which) throws RemoteException { + if (which != FLAG_LOCK && which != FLAG_SYSTEM) { + throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM"); + } + + WallpaperData wallpaperData = null; + boolean shouldExtract; + + synchronized (mLock) { + if (which == FLAG_LOCK) { + wallpaperData = mLockWallpaperMap.get(mCurrentUserId); + } + + // Try to get the system wallpaper anyway since it might + // also be the lock screen wallpaper + if (wallpaperData == null) { + wallpaperData = mWallpaperMap.get(mCurrentUserId); + } + + if (wallpaperData == null) { + return null; + } + shouldExtract = wallpaperData.primaryColors == null; + } + + if (shouldExtract) { + extractColors(wallpaperData); + } + + synchronized (mLock) { + return wallpaperData.primaryColors; + } + } + @Override public ParcelFileDescriptor setWallpaper(String name, String callingPackage, Rect cropHint, boolean allowBackup, Bundle extras, int which, @@ -1509,6 +1697,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { lockWP.width = sysWP.width; lockWP.height = sysWP.height; lockWP.allowBackup = sysWP.allowBackup; + lockWP.primaryColors = sysWP.primaryColors; // Migrate the bitmap files outright; no need to copy try { @@ -1546,6 +1735,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (extras != null) { extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId); } + // Nullify field to require new computation + wallpaper.primaryColors = null; if (DEBUG) { Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId + " name=" + name + " file=" + wallpaper.wallpaperFile.getName()); @@ -1577,9 +1768,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { false /* all */, true /* full */, "changing live wallpaper", null /* pkg */); checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); + int which = FLAG_SYSTEM; + boolean shouldNotifyColors = false; + WallpaperData wallpaper; + synchronized (mLock) { if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); - WallpaperData wallpaper = mWallpaperMap.get(userId); + wallpaper = mWallpaperMap.get(userId); if (wallpaper == null) { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); } @@ -1597,16 +1792,26 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } } + // New live wallpaper is also a lock wallpaper if nothing is set + if (mLockWallpaperMap.get(userId) == null) { + which |= FLAG_LOCK; + } + try { wallpaper.imageWallpaperPending = false; if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) { wallpaper.wallpaperId = makeWallpaperIdLocked(); notifyCallbacksLocked(wallpaper); + shouldNotifyColors = true; } } finally { Binder.restoreCallingIdentity(ident); } } + + if (shouldNotifyColors) { + notifyWallpaperColorsChanged(wallpaper, which); + } } boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, @@ -1731,6 +1936,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } wallpaper.wallpaperComponent = componentName; wallpaper.connection = newConn; + wallpaper.primaryColors = null; newConn.mReply = reply; try { if (wallpaper.userId == mCurrentUserId) { @@ -1810,6 +2016,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } } wallpaper.callbacks.finishBroadcast(); + final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED); mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId)); } @@ -1900,6 +2107,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper) throws IllegalArgumentException, IllegalStateException, IOException { + if (DEBUG) { + Slog.v(TAG, "writeWallpaperAttributes"); + } out.startTag(null, tag); out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId)); out.attribute(null, "width", Integer.toString(wallpaper.width)); @@ -1923,6 +2133,20 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom)); } + if (wallpaper.primaryColors != null) { + int colorsCount = wallpaper.primaryColors.getColors().size(); + out.attribute(null, "colorsCount", Integer.toString(colorsCount)); + if (colorsCount > 0) { + for (int i = 0; i < colorsCount; i++) { + Pair wc = wallpaper.primaryColors.getColors().get(i); + out.attribute(null, "colorValue"+i, Integer.toString(wc.first.toArgb())); + out.attribute(null, "colorWeight"+i, Integer.toString(wc.second)); + } + } + out.attribute(null, "supportsDarkText", + Integer.toString(wallpaper.primaryColors.supportsDarkText() ? 1 : 0)); + } + out.attribute(null, "name", wallpaper.name); if (wallpaper.wallpaperComponent != null && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) { @@ -2054,6 +2278,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { Slog.v(TAG, "mWidth:" + wallpaper.width); Slog.v(TAG, "mHeight:" + wallpaper.height); Slog.v(TAG, "cropRect:" + wallpaper.cropHint); + Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors); Slog.v(TAG, "mName:" + wallpaper.name); Slog.v(TAG, "mNextWallpaperComponent:" + wallpaper.nextWallpaperComponent); @@ -2152,6 +2377,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0); wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0); wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0); + int colorsCount = getAttributeInt(parser, "colorsCount", 0); + if (colorsCount > 0) { + List> colors = new ArrayList<>(); + for (int i = 0; i < colorsCount; i++) { + colors.add(new Pair<>( + Color.valueOf(getAttributeInt(parser, "colorValue"+i, 0)), + getAttributeInt(parser, "colorWeight"+i, 0) + )); + } + boolean dark = getAttributeInt(parser, "supportsDarkText", 0) == 1; + wallpaper.primaryColors = new WallpaperColors(colors, dark); + } wallpaper.name = parser.getAttributeValue(null, "name"); wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup")); }