diff --git a/packages/SystemUI/res/drawable/sb_date_bg9.xml b/packages/SystemUI/res/drawable/sb_date_bg9.xml index 2bd20dd9939f8..fba9b2c2ff453 100644 --- a/packages/SystemUI/res/drawable/sb_date_bg9.xml +++ b/packages/SystemUI/res/drawable/sb_date_bg9.xml @@ -2,7 +2,7 @@ - + diff --git a/packages/SystemUI/src/com/android/systemui/clocks/ClockStyle.kt b/packages/SystemUI/src/com/android/systemui/clocks/ClockStyle.kt index a59419c8d0ec1..2f1b8adc20fe7 100644 --- a/packages/SystemUI/src/com/android/systemui/clocks/ClockStyle.kt +++ b/packages/SystemUI/src/com/android/systemui/clocks/ClockStyle.kt @@ -8,6 +8,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.res.Configuration import android.graphics.Color import android.graphics.Typeface import android.os.Handler @@ -165,6 +166,11 @@ class ClockStyle @JvmOverloads constructor( callbacksRegistered = false } + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + updateClockView() + } + override fun onTuningChanged(key: String?, newValue: String?) { when (key) { CLOCK_STYLE_KEY -> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt index 7ebac0a62d4a5..a611db9b7d1f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt @@ -36,6 +36,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators import com.android.systemui.crdroid.batterybar.BatteryBarController import com.android.systemui.crdroid.logo.LogoImage +import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.DisplayAware import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.PerDisplaySingleton import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R @@ -62,6 +63,7 @@ import com.android.systemui.statusbar.phone.ui.StatusBarIconController import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel import com.android.systemui.statusbar.policy.Clock +import com.android.systemui.statusbar.policy.ConfigurationController import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow @@ -98,7 +100,8 @@ interface HomeStatusBarViewBinder { class HomeStatusBarViewBinderImpl @Inject constructor( - private val viewStoreFactory: ConnectedDisplaysStatusBarNotificationIconViewStore.Factory + private val viewStoreFactory: ConnectedDisplaysStatusBarNotificationIconViewStore.Factory, + @DisplayAware private val configurationController: ConfigurationController, ) : HomeStatusBarViewBinder { private companion object { private const val CLOCK_POSITION_RIGHT = 0 @@ -111,6 +114,7 @@ constructor( val denyListed: Boolean, val hideForHun: Boolean, val chipStyle: Int, + val themeVersion: Int, val position: Int, val visibilityModel: VisibilityModel, ) @@ -166,6 +170,7 @@ constructor( denyListed = false, hideForHun = false, chipStyle = 0, + themeVersion = 0, position = context.contentResolver.readClockPosition(), visibilityModel = VisibilityModel(View.GONE, true), ) @@ -247,7 +252,12 @@ constructor( } } - val urisToObserve = listOf(clockAutoHideUri, iconHideListUri, statusBarClockUri, statusBarClockChipUri) + val urisToObserve = listOf( + clockAutoHideUri, + iconHideListUri, + statusBarClockUri, + statusBarClockChipUri, + ) urisToObserve.forEach { uri -> context.contentResolver.registerContentObserver( uri, @@ -258,11 +268,22 @@ constructor( contentObserver.onChange(false, uri) } + val configurationListener = + object : ConfigurationController.ConfigurationListener { + override fun onThemeChanged() { + clockState.update { current -> + current.copy(themeVersion = current.themeVersion + 1) + } + } + } + configurationController.addCallback(configurationListener) + // Ensure cleanup when lifecycle ends val job = coroutineContext[Job] job?.invokeOnCompletion { runCatching { context.contentResolver.unregisterContentObserver(contentObserver) + configurationController.removeCallback(configurationListener) TaskStackChangeListeners.getInstance() .unregisterTaskStackListener(taskStackListener) } @@ -465,6 +486,7 @@ constructor( launch { var lastChipStyle: Int? = null var lastClockPosition: Int? = null + var lastThemeVersion: Int? = null clockState.collect { state -> // We only want to hide left clock for HUN @@ -504,6 +526,7 @@ constructor( // Only touch chip UI when needed val chipNeedsUpdate = (lastChipStyle != state.chipStyle) || (lastClockPosition != state.position) + || (lastThemeVersion != state.themeVersion) if (chipNeedsUpdate) { applyClockChip( context = context, @@ -518,6 +541,7 @@ constructor( ) lastChipStyle = state.chipStyle lastClockPosition = state.position + lastThemeVersion = state.themeVersion } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt index 0a9f7701c3497..2f8a043c2e335 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt @@ -47,11 +47,15 @@ import com.android.systemui.statusbar.pipeline.shared.domain.interactor.homeStat import com.android.systemui.statusbar.pipeline.shared.domain.interactor.homeStatusBarInteractor import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinderImpl +import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.statusbar.systemstatusicons.ui.viewmodel.systemStatusIconsViewModelFactory var Kosmos.homeStatusBarViewBinder: HomeStatusBarViewBinder by Kosmos.Fixture { - HomeStatusBarViewBinderImpl(connectedDisplaysStatusBarNotificationIconViewStoreFactory) + HomeStatusBarViewBinderImpl( + connectedDisplaysStatusBarNotificationIconViewStoreFactory, + configurationController, + ) } var Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 03697a461f6d2..8f5197193d374 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -82,6 +82,8 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; @@ -225,6 +227,7 @@ public void onUserUnlocking(@NonNull TargetUser user) { */ private static final long MIN_WALLPAPER_CRASH_TIME = 10000; private static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128; + private static final int WALLPAPER_COLOR_BITMAP_MAX_AREA = 112 * 112; /** * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks @@ -2980,6 +2983,7 @@ public WallpaperColors getWallpaperColors(int which, int userId, int displayId) userId, false, true, "getWallpaperColors", null); WallpaperData wallpaperData = null; + boolean shouldExtract; synchronized (mLock) { if (which == FLAG_LOCK) { @@ -2995,11 +2999,79 @@ public WallpaperColors getWallpaperColors(int which, int userId, int displayId) if (wallpaperData == null) { return null; } + shouldExtract = wallpaperData.primaryColors == null; + } + + if (shouldExtract && extractStaticWallpaperColors(wallpaperData)) { + notifyWallpaperColorsChangedOnDisplay(wallpaperData, displayId); } return getAdjustedWallpaperColorsOnDimming(wallpaperData); } + /** + * Fallback for a static ImageWallpaper engine that did not publish its initial colors. + * + * @return {@code true} when colors were extracted and stored for the current wallpaper. + */ + private boolean extractStaticWallpaperColors(WallpaperData wallpaper) { + final String cropFile; + final int wallpaperId; + final float dimAmount; + + synchronized (mLock) { + if (wallpaper.primaryColors != null) { + return false; + } + final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.getComponent()) + || wallpaper.getComponent() == null; + if (!imageWallpaper || !wallpaper.getCropFile().exists()) { + return false; + } + cropFile = wallpaper.getCropFile().getAbsolutePath(); + wallpaperId = wallpaper.wallpaperId; + dimAmount = wallpaper.mWallpaperDimAmount; + } + + final BitmapFactory.Options bounds = new BitmapFactory.Options(); + bounds.inJustDecodeBounds = true; + BitmapFactory.decodeFile(cropFile, bounds); + if (bounds.outWidth <= 0 || bounds.outHeight <= 0) { + Slog.w(TAG, "Cannot extract colors because static wallpaper bounds are invalid"); + return false; + } + + int sampleSize = 1; + while ((long) (bounds.outWidth / sampleSize) * (bounds.outHeight / sampleSize) + > WALLPAPER_COLOR_BITMAP_MAX_AREA) { + sampleSize *= 2; + } + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = sampleSize; + final Bitmap bitmap = BitmapFactory.decodeFile(cropFile, options); + if (bitmap == null) { + Slog.w(TAG, "Cannot extract colors because static wallpaper could not be read"); + return false; + } + + final WallpaperColors colors; + try { + colors = WallpaperColors.fromBitmap(bitmap, dimAmount); + } finally { + bitmap.recycle(); + } + + synchronized (mLock) { + if (wallpaper.wallpaperId != wallpaperId || wallpaper.primaryColors != null) { + return false; + } + wallpaper.primaryColors = colors; + wallpaper.mIsColorExtractedFromDim = false; + saveSettingsLocked(wallpaper.userId); + return true; + } + } + /** * Gets the adjusted {@link WallpaperColors} if the wallpaper colors were not extracted from * bitmap (i.e. it's a live wallpaper) and the dim amount is not 0. If these conditions apply,