Merge "Move color extraction to ImageWallpaper" into main
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 0fc51e7..582c90f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -27,12 +27,15 @@
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.window.flags.Flags.FLAG_OFFLOAD_COLOR_EXTRACTION;
import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents;
+import static com.android.window.flags.Flags.offloadColorExtraction;
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -832,6 +835,15 @@
}
/**
+ * Called when the dim amount of the wallpaper changed. This can be used to recompute the
+ * wallpaper colors based on the new dim, and call {@link #notifyColorsChanged()}.
+ * @hide
+ */
+ @FlaggedApi(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void onDimAmountChanged(float dimAmount) {
+ }
+
+ /**
* Called when an application has changed the desired virtual size of
* the wallpaper.
*/
@@ -1043,6 +1055,10 @@
}
mPreviousWallpaperDimAmount = mWallpaperDimAmount;
+
+ // after the dim changes, allow colors to be immediately recomputed
+ mLastColorInvalidation = 0;
+ if (offloadColorExtraction()) onDimAmountChanged(mWallpaperDimAmount);
}
/**
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index aa92af2..150b04e 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -21,4 +21,14 @@
namespace: "systemui"
description: "Prevent the system from sending consecutive onVisibilityChanged(false) events."
bug: "285631818"
+}
+
+flag {
+ name: "offload_color_extraction"
+ namespace: "systemui"
+ description: "Let ImageWallpaper take care of its wallpaper color extraction, instead of system_server"
+ bug: "328791519"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index d00081e..1568e8c0 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -20,6 +20,9 @@
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.app.WallpaperManager.SetWallpaperFlags;
+import static com.android.window.flags.Flags.offloadColorExtraction;
+
+import android.annotation.Nullable;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
@@ -135,6 +138,12 @@
mLongExecutor,
mLock,
new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
+
+ @Override
+ public void onColorsProcessed() {
+ CanvasEngine.this.notifyColorsChanged();
+ }
+
@Override
public void onColorsProcessed(List<RectF> regions,
List<WallpaperColors> colors) {
@@ -433,6 +442,12 @@
}
@Override
+ public @Nullable WallpaperColors onComputeColors() {
+ if (!offloadColorExtraction()) return null;
+ return mWallpaperLocalColorExtractor.onComputeColors();
+ }
+
+ @Override
public boolean supportsLocalColorExtraction() {
return true;
}
@@ -469,6 +484,12 @@
}
@Override
+ public void onDimAmountChanged(float dimAmount) {
+ if (!offloadColorExtraction()) return;
+ mWallpaperLocalColorExtractor.onDimAmountChanged(dimAmount);
+ }
+
+ @Override
public void onDisplayAdded(int displayId) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
index e2ec8dc..d37dfb4 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
@@ -17,6 +17,8 @@
package com.android.systemui.wallpapers;
+import static com.android.window.flags.Flags.offloadColorExtraction;
+
import android.app.WallpaperColors;
import android.graphics.Bitmap;
import android.graphics.Rect;
@@ -66,6 +68,12 @@
private final List<RectF> mPendingRegions = new ArrayList<>();
private final Set<RectF> mProcessedRegions = new ArraySet<>();
+ private float mWallpaperDimAmount = 0f;
+ private WallpaperColors mWallpaperColors;
+
+ // By default we assume that colors were loaded from disk and don't need to be recomputed
+ private boolean mRecomputeColors = false;
+
@LongRunning
private final Executor mLongExecutor;
@@ -75,6 +83,12 @@
* Interface to handle the callbacks after the different steps of the color extraction
*/
public interface WallpaperLocalColorExtractorCallback {
+
+ /**
+ * Callback after the wallpaper colors have been computed
+ */
+ void onColorsProcessed();
+
/**
* Callback after the colors of new regions have been extracted
* @param regions the list of new regions that have been processed
@@ -129,7 +143,7 @@
if (displayWidth == mDisplayWidth && displayHeight == mDisplayHeight) return;
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
- processColorsInternal();
+ processLocalColorsInternal();
}
}
@@ -166,7 +180,8 @@
mBitmapHeight = bitmap.getHeight();
mMiniBitmap = createMiniBitmap(bitmap);
mWallpaperLocalColorExtractorCallback.onMiniBitmapUpdated();
- recomputeColors();
+ if (offloadColorExtraction() && mRecomputeColors) recomputeColorsInternal();
+ recomputeLocalColors();
}
}
@@ -184,16 +199,66 @@
if (mPages == pages) return;
mPages = pages;
if (mMiniBitmap != null && !mMiniBitmap.isRecycled()) {
- recomputeColors();
+ recomputeLocalColors();
}
}
}
- // helper to recompute colors, to be called in synchronized methods
- private void recomputeColors() {
+ /**
+ * Should be called when the dim amount of the wallpaper changes, to recompute the colors
+ */
+ public void onDimAmountChanged(float dimAmount) {
+ mLongExecutor.execute(() -> onDimAmountChangedSynchronized(dimAmount));
+ }
+
+ private void onDimAmountChangedSynchronized(float dimAmount) {
+ synchronized (mLock) {
+ if (mWallpaperDimAmount == dimAmount) return;
+ mWallpaperDimAmount = dimAmount;
+ mRecomputeColors = true;
+ recomputeColorsInternal();
+ }
+ }
+
+ /**
+ * To be called by {@link ImageWallpaper.CanvasEngine#onComputeColors}. This will either
+ * return the current wallpaper colors, or if the bitmap is not yet loaded, return null and call
+ * {@link WallpaperLocalColorExtractorCallback#onColorsProcessed()} when the colors are ready.
+ */
+ public WallpaperColors onComputeColors() {
+ mLongExecutor.execute(this::onComputeColorsSynchronized);
+ return mWallpaperColors;
+ }
+
+ private void onComputeColorsSynchronized() {
+ synchronized (mLock) {
+ if (mRecomputeColors) return;
+ mRecomputeColors = true;
+ recomputeColorsInternal();
+ }
+ }
+
+ /**
+ * helper to recompute main colors, to be called in synchronized methods
+ */
+ private void recomputeColorsInternal() {
+ if (mMiniBitmap == null) return;
+ mWallpaperColors = getWallpaperColors(mMiniBitmap, mWallpaperDimAmount);
+ mWallpaperLocalColorExtractorCallback.onColorsProcessed();
+ }
+
+ @VisibleForTesting
+ WallpaperColors getWallpaperColors(@NonNull Bitmap bitmap, float dimAmount) {
+ return WallpaperColors.fromBitmap(bitmap, dimAmount);
+ }
+
+ /**
+ * helper to recompute local colors, to be called in synchronized methods
+ */
+ private void recomputeLocalColors() {
mPendingRegions.addAll(mProcessedRegions);
mProcessedRegions.clear();
- processColorsInternal();
+ processLocalColorsInternal();
}
/**
@@ -216,7 +281,7 @@
if (!wasActive && isActive()) {
mWallpaperLocalColorExtractorCallback.onActivated();
}
- processColorsInternal();
+ processLocalColorsInternal();
}
}
@@ -353,7 +418,7 @@
* then notify the callback with the resulting colors for these regions
* This method should only be called synchronously
*/
- private void processColorsInternal() {
+ private void processLocalColorsInternal() {
/*
* if the miniBitmap is not yet loaded, that means the onBitmapChanged has not yet been
* called, and thus the wallpaper is not yet loaded. In that case, exit, the function
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
index 1125d41..0eba21a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
@@ -16,9 +16,12 @@
package com.android.systemui.wallpapers;
+import static com.android.window.flags.Flags.FLAG_OFFLOAD_COLOR_EXTRACTION;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -32,6 +35,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -77,6 +81,7 @@
private Executor mBackgroundExecutor;
private int mColorsProcessed;
+ private int mLocalColorsProcessed;
private int mMiniBitmapUpdatedCount;
private int mActivatedCount;
private int mDeactivatedCount;
@@ -93,6 +98,7 @@
private void resetCounters() {
mColorsProcessed = 0;
+ mLocalColorsProcessed = 0;
mMiniBitmapUpdatedCount = 0;
mActivatedCount = 0;
mDeactivatedCount = 0;
@@ -112,10 +118,14 @@
new Object(),
new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
@Override
+ public void onColorsProcessed() {
+ mColorsProcessed++;
+ }
+ @Override
public void onColorsProcessed(List<RectF> regions,
List<WallpaperColors> colors) {
assertThat(regions.size()).isEqualTo(colors.size());
- mColorsProcessed += regions.size();
+ mLocalColorsProcessed += regions.size();
}
@Override
@@ -148,8 +158,10 @@
.when(spyColorExtractor)
.createMiniBitmap(any(Bitmap.class), anyInt(), anyInt());
- doReturn(new WallpaperColors(Color.valueOf(0), Color.valueOf(0), Color.valueOf(0)))
- .when(spyColorExtractor).getLocalWallpaperColors(any(Rect.class));
+ WallpaperColors colors = new WallpaperColors(
+ Color.valueOf(0), Color.valueOf(0), Color.valueOf(0));
+ doReturn(colors).when(spyColorExtractor).getLocalWallpaperColors(any(Rect.class));
+ doReturn(colors).when(spyColorExtractor).getWallpaperColors(any(Bitmap.class), anyFloat());
return spyColorExtractor;
}
@@ -244,7 +256,7 @@
assertThat(mActivatedCount).isEqualTo(1);
assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
- assertThat(mColorsProcessed).isEqualTo(regions.size());
+ assertThat(mLocalColorsProcessed).isEqualTo(regions.size());
spyColorExtractor.removeLocalColorAreas(regions);
assertThat(mDeactivatedCount).isEqualTo(1);
@@ -329,12 +341,69 @@
spyColorExtractor.onBitmapChanged(newBitmap);
assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
}
- assertThat(mColorsProcessed).isEqualTo(regions.size());
+ assertThat(mLocalColorsProcessed).isEqualTo(regions.size());
}
spyColorExtractor.removeLocalColorAreas(regions);
assertThat(mDeactivatedCount).isEqualTo(1);
}
+ /**
+ * Test that after the bitmap changes, the colors are computed only if asked via onComputeColors
+ */
+ @Test
+ @EnableFlags(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void testRecomputeColors() {
+ resetCounters();
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ WallpaperLocalColorExtractor spyColorExtractor = getSpyWallpaperLocalColorExtractor();
+ spyColorExtractor.onBitmapChanged(bitmap);
+ assertThat(mColorsProcessed).isEqualTo(0);
+ spyColorExtractor.onComputeColors();
+ assertThat(mColorsProcessed).isEqualTo(1);
+ }
+
+ /**
+ * Test that after onComputeColors is called, the colors are computed once the bitmap is loaded
+ */
+ @Test
+ @EnableFlags(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void testRecomputeColorsBeforeBitmapLoaded() {
+ resetCounters();
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ WallpaperLocalColorExtractor spyColorExtractor = getSpyWallpaperLocalColorExtractor();
+ spyColorExtractor.onComputeColors();
+ spyColorExtractor.onBitmapChanged(bitmap);
+ assertThat(mColorsProcessed).isEqualTo(1);
+ }
+
+ /**
+ * Test that after the dim changes, the colors are computed if the bitmap is already loaded
+ */
+ @Test
+ @EnableFlags(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void testRecomputeColorsOnDimChanged() {
+ resetCounters();
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ WallpaperLocalColorExtractor spyColorExtractor = getSpyWallpaperLocalColorExtractor();
+ spyColorExtractor.onBitmapChanged(bitmap);
+ spyColorExtractor.onDimAmountChanged(0.5f);
+ assertThat(mColorsProcessed).isEqualTo(1);
+ }
+
+ /**
+ * Test that after the dim changes, the colors will be recomputed once the bitmap is loaded
+ */
+ @Test
+ @EnableFlags(FLAG_OFFLOAD_COLOR_EXTRACTION)
+ public void testRecomputeColorsOnDimChangedBeforeBitmapLoaded() {
+ resetCounters();
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ WallpaperLocalColorExtractor spyColorExtractor = getSpyWallpaperLocalColorExtractor();
+ spyColorExtractor.onDimAmountChanged(0.3f);
+ spyColorExtractor.onBitmapChanged(bitmap);
+ assertThat(mColorsProcessed).isEqualTo(1);
+ }
+
@Test
public void testCleanUp() {
resetCounters();
@@ -346,6 +415,6 @@
assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
spyColorExtractor.cleanUp();
spyColorExtractor.addLocalColorsAreas(listOfRandomAreas(MIN_AREAS, MAX_AREAS));
- assertThat(mColorsProcessed).isEqualTo(0);
+ assertThat(mLocalColorsProcessed).isEqualTo(0);
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 8c4c0de..802f196 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -42,6 +42,7 @@
import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
import static com.android.server.wallpaper.WallpaperUtils.makeWallpaperIdLocked;
import static com.android.window.flags.Flags.multiCrop;
+import static com.android.window.flags.Flags.offloadColorExtraction;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -380,7 +381,7 @@
}
// Outside of the lock since it will synchronize itself
- notifyWallpaperColorsChanged(wallpaper);
+ if (!offloadColorExtraction()) notifyWallpaperColorsChanged(wallpaper);
}
@Override
@@ -403,12 +404,16 @@
}
void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper) {
+ notifyWallpaperColorsChanged(wallpaper, wallpaper.mWhich);
+ }
+
+ private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
if (DEBUG) {
Slog.i(TAG, "Notifying wallpaper colors changed");
}
if (wallpaper.connection != null) {
wallpaper.connection.forEachDisplayConnector(connector ->
- notifyWallpaperColorsChangedOnDisplay(wallpaper, connector.mDisplayId));
+ notifyWallpaperColorsChangedOnDisplay(wallpaper, connector.mDisplayId, which));
}
}
@@ -425,6 +430,11 @@
private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper,
int displayId) {
+ notifyWallpaperColorsChangedOnDisplay(wallpaper, displayId, wallpaper.mWhich);
+ }
+
+ private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper,
+ int displayId, int which) {
boolean needsExtraction;
synchronized (mLock) {
final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
@@ -449,8 +459,8 @@
notify = extractColors(wallpaper);
}
if (notify) {
- notifyColorListeners(getAdjustedWallpaperColorsOnDimming(wallpaper),
- wallpaper.mWhich, wallpaper.userId, displayId);
+ notifyColorListeners(getAdjustedWallpaperColorsOnDimming(wallpaper), which,
+ wallpaper.userId, displayId);
}
}
@@ -504,6 +514,7 @@
* @return true unless the wallpaper changed during the color computation
*/
private boolean extractColors(WallpaperData wallpaper) {
+ if (offloadColorExtraction()) return !mImageWallpaper.equals(wallpaper.wallpaperComponent);
String cropFile = null;
boolean defaultImageWallpaper = false;
int wallpaperId;
@@ -1148,10 +1159,16 @@
synchronized (mLock) {
// Do not broadcast changes on ImageWallpaper since it's handled
// internally by this class.
- if (mImageWallpaper.equals(mWallpaper.wallpaperComponent)) {
+ boolean isImageWallpaper = mImageWallpaper.equals(mWallpaper.wallpaperComponent);
+ if (isImageWallpaper && (!offloadColorExtraction() || primaryColors == null)) {
return;
}
mWallpaper.primaryColors = primaryColors;
+ // only save the colors for ImageWallpaper - for live wallpapers, the colors
+ // are always recomputed after a reboot.
+ if (offloadColorExtraction() && isImageWallpaper) {
+ saveSettingsLocked(mWallpaper.userId);
+ }
}
notifyWallpaperColorsChangedOnDisplay(mWallpaper, displayId);
}
@@ -1177,7 +1194,9 @@
try {
// This will trigger onComputeColors in the wallpaper engine.
// It's fine to be locked in here since the binder is oneway.
- connector.mEngine.requestWallpaperColors();
+ if (!offloadColorExtraction() || mWallpaper.primaryColors == null) {
+ connector.mEngine.requestWallpaperColors();
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Failed to request wallpaper colors", e);
}
@@ -1811,6 +1830,7 @@
// Offload color extraction to another thread since switchUser will be called
// from the main thread.
FgThread.getHandler().post(() -> {
+ if (offloadColorExtraction()) return;
notifyWallpaperColorsChanged(systemWallpaper);
if (lockWallpaper != systemWallpaper) notifyWallpaperColorsChanged(lockWallpaper);
notifyWallpaperColorsChanged(mFallbackWallpaper);
@@ -2722,8 +2742,10 @@
});
// Need to extract colors again to re-calculate dark hints after
// applying dimming.
- wp.mIsColorExtractedFromDim = true;
- pendingColorExtraction.add(wp);
+ if (!offloadColorExtraction()) {
+ wp.mIsColorExtractedFromDim = true;
+ pendingColorExtraction.add(wp);
+ }
changed = true;
}
}
@@ -2732,7 +2754,7 @@
}
}
for (WallpaperData wp: pendingColorExtraction) {
- notifyWallpaperColorsChanged(wp);
+ if (!offloadColorExtraction()) notifyWallpaperColorsChanged(wp);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2927,6 +2949,7 @@
}
wallpaper.allowBackup = allowBackup;
wallpaper.mWallpaperDimAmount = getWallpaperDimAmount();
+ if (offloadColorExtraction()) wallpaper.primaryColors = null;
}
return pfd;
} finally {
@@ -3069,6 +3092,10 @@
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
boolean shouldNotifyColors = false;
+
+ // If the lockscreen wallpaper is set to the same as the home screen, notify that the
+ // lockscreen wallpaper colors changed, even if we don't bind a new wallpaper engine.
+ boolean shouldNotifyLockscreenColors = false;
boolean bindSuccess;
final WallpaperData newWallpaper;
@@ -3114,7 +3141,7 @@
bindSuccess = bindWallpaperComponentLocked(name, /* force */
forceRebind, /* fromUser */ true, newWallpaper, reply);
if (bindSuccess) {
- if (!same) {
+ if (!same || (offloadColorExtraction() && forceRebind)) {
newWallpaper.primaryColors = null;
} else {
if (newWallpaper.connection != null) {
@@ -3138,6 +3165,11 @@
newWallpaper.wallpaperId = makeWallpaperIdLocked();
notifyCallbacksLocked(newWallpaper);
shouldNotifyColors = true;
+ if (offloadColorExtraction()) {
+ shouldNotifyColors = false;
+ shouldNotifyLockscreenColors = !force && same && !systemIsBoth
+ && which == (FLAG_SYSTEM | FLAG_LOCK);
+ }
if (which == (FLAG_SYSTEM | FLAG_LOCK)) {
if (DEBUG) {
@@ -3166,6 +3198,10 @@
if (shouldNotifyColors) {
notifyWallpaperColorsChanged(newWallpaper);
}
+ if (shouldNotifyLockscreenColors) {
+ notifyWallpaperColorsChanged(newWallpaper, FLAG_LOCK);
+ }
+
return bindSuccess;
}