Merge "Send messages to Launcher preview when previewing color" into main
diff --git a/src/com/android/customization/model/color/ColorOption.java b/src/com/android/customization/model/color/ColorOption.java
index ae695dd..d3886a0 100644
--- a/src/com/android/customization/model/color/ColorOption.java
+++ b/src/com/android/customization/model/color/ColorOption.java
@@ -103,6 +103,23 @@
}
/**
+ * Gets the seed color from the overlay packages in hex string.
+ *
+ * @return a string representing the seed color, or null if the color option is generated from
+ * the default seed.
+ */
+ public Integer getSeedColor() {
+ String seedColor = mPackagesByCategory.get(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ if (TextUtils.isEmpty(seedColor)) {
+ return null;
+ }
+ if (!seedColor.startsWith("#")) {
+ seedColor = "#" + seedColor;
+ }
+ return Color.parseColor(seedColor);
+ }
+
+ /**
* Gets the seed color from the overlay packages for logging.
*
* @return an int representing the seed color, or NULL_SEED_COLOR
diff --git a/src/com/android/customization/picker/color/data/util/MaterialColorsGenerator.kt b/src/com/android/customization/picker/color/data/util/MaterialColorsGenerator.kt
new file mode 100644
index 0000000..a921365
--- /dev/null
+++ b/src/com/android/customization/picker/color/data/util/MaterialColorsGenerator.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2024 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 com.android.customization.picker.color.data.util
+
+import android.app.WallpaperColors
+import android.content.Context
+import android.content.res.Configuration
+import android.provider.Settings
+import android.util.Log
+import android.util.SparseIntArray
+import com.android.customization.model.ResourceConstants
+import com.android.systemui.monet.ColorScheme
+import com.android.systemui.monet.Style
+import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
+import dagger.hilt.android.qualifiers.ApplicationContext
+import javax.inject.Inject
+import javax.inject.Singleton
+import org.json.JSONException
+import org.json.JSONObject
+
+/**
+ * Extract material next colors from wallpaper colors. Based on Nexus Launcher's
+ * MaterialColorsGenerator, nexuslauncher/widget/MaterialColorsGenerator.java
+ */
+@Singleton
+class MaterialColorsGenerator
+@Inject
+constructor(
+ @ApplicationContext private val applicationContext: Context,
+ private val secureSettingsRepository: SecureSettingsRepository,
+) {
+ private fun addShades(shades: List<Int>, resources: IntArray, output: SparseIntArray) {
+ if (shades.size != resources.size) {
+ Log.e(TAG, "The number of shades computed doesn't match the number of resources.")
+ return
+ }
+ for (i in resources.indices) {
+ output.put(resources[i], 0xff000000.toInt() or shades[i])
+ }
+ }
+
+ /**
+ * Generates the mapping from system color resources to values from wallpaper colors.
+ *
+ * @return a list of color resource IDs and a corresponding list of their color values
+ */
+ suspend fun generate(colors: WallpaperColors): Pair<IntArray, IntArray> {
+ val isDarkMode =
+ (applicationContext.resources.configuration.uiMode and
+ Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
+ val colorScheme = ColorScheme(colors, isDarkMode, fetchThemeStyleFromSetting())
+ return generate(colorScheme)
+ }
+
+ /**
+ * Generates the mapping from system color resources to values from color seed and style.
+ *
+ * @return a list of color resource IDs and a corresponding list of their color values
+ */
+ fun generate(colorSeed: Int, style: Style): Pair<IntArray, IntArray> {
+ val isDarkMode =
+ (applicationContext.resources.configuration.uiMode and
+ Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
+ val colorScheme = ColorScheme(colorSeed, isDarkMode, style)
+ return generate(colorScheme)
+ }
+
+ private fun generate(colorScheme: ColorScheme): Pair<IntArray, IntArray> {
+ val allNeutralColors: MutableList<Int> = ArrayList()
+ allNeutralColors.addAll(colorScheme.neutral1.allShades)
+ allNeutralColors.addAll(colorScheme.neutral2.allShades)
+
+ val allAccentColors: MutableList<Int> = ArrayList()
+ allAccentColors.addAll(colorScheme.accent1.allShades)
+ allAccentColors.addAll(colorScheme.accent2.allShades)
+ allAccentColors.addAll(colorScheme.accent3.allShades)
+
+ return Pair(
+ NEUTRAL_RESOURCES + ACCENT_RESOURCES,
+ (allNeutralColors + allAccentColors).toIntArray(),
+ )
+ }
+
+ private suspend fun fetchThemeStyleFromSetting(): Style {
+ val overlayPackageJson =
+ secureSettingsRepository.getString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)
+ return if (!overlayPackageJson.isNullOrEmpty()) {
+ try {
+ val jsonObject = JSONObject(overlayPackageJson)
+ Style.valueOf(jsonObject.getString(ResourceConstants.OVERLAY_CATEGORY_THEME_STYLE))
+ } catch (e: (JSONException)) {
+ Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e)
+ Style.TONAL_SPOT
+ } catch (e: IllegalArgumentException) {
+ Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e)
+ Style.TONAL_SPOT
+ }
+ } else {
+ Style.TONAL_SPOT
+ }
+ }
+
+ companion object {
+ private const val TAG = "MaterialColorsGenerator"
+
+ private val ACCENT_RESOURCES =
+ intArrayOf(
+ android.R.color.system_accent1_0,
+ android.R.color.system_accent1_10,
+ android.R.color.system_accent1_50,
+ android.R.color.system_accent1_100,
+ android.R.color.system_accent1_200,
+ android.R.color.system_accent1_300,
+ android.R.color.system_accent1_400,
+ android.R.color.system_accent1_500,
+ android.R.color.system_accent1_600,
+ android.R.color.system_accent1_700,
+ android.R.color.system_accent1_800,
+ android.R.color.system_accent1_900,
+ android.R.color.system_accent1_1000,
+ android.R.color.system_accent2_0,
+ android.R.color.system_accent2_10,
+ android.R.color.system_accent2_50,
+ android.R.color.system_accent2_100,
+ android.R.color.system_accent2_200,
+ android.R.color.system_accent2_300,
+ android.R.color.system_accent2_400,
+ android.R.color.system_accent2_500,
+ android.R.color.system_accent2_600,
+ android.R.color.system_accent2_700,
+ android.R.color.system_accent2_800,
+ android.R.color.system_accent2_900,
+ android.R.color.system_accent2_1000,
+ android.R.color.system_accent3_0,
+ android.R.color.system_accent3_10,
+ android.R.color.system_accent3_50,
+ android.R.color.system_accent3_100,
+ android.R.color.system_accent3_200,
+ android.R.color.system_accent3_300,
+ android.R.color.system_accent3_400,
+ android.R.color.system_accent3_500,
+ android.R.color.system_accent3_600,
+ android.R.color.system_accent3_700,
+ android.R.color.system_accent3_800,
+ android.R.color.system_accent3_900,
+ android.R.color.system_accent3_1000,
+ )
+ private val NEUTRAL_RESOURCES =
+ intArrayOf(
+ android.R.color.system_neutral1_0,
+ android.R.color.system_neutral1_10,
+ android.R.color.system_neutral1_50,
+ android.R.color.system_neutral1_100,
+ android.R.color.system_neutral1_200,
+ android.R.color.system_neutral1_300,
+ android.R.color.system_neutral1_400,
+ android.R.color.system_neutral1_500,
+ android.R.color.system_neutral1_600,
+ android.R.color.system_neutral1_700,
+ android.R.color.system_neutral1_800,
+ android.R.color.system_neutral1_900,
+ android.R.color.system_neutral1_1000,
+ android.R.color.system_neutral2_0,
+ android.R.color.system_neutral2_10,
+ android.R.color.system_neutral2_50,
+ android.R.color.system_neutral2_100,
+ android.R.color.system_neutral2_200,
+ android.R.color.system_neutral2_300,
+ android.R.color.system_neutral2_400,
+ android.R.color.system_neutral2_500,
+ android.R.color.system_neutral2_600,
+ android.R.color.system_neutral2_700,
+ android.R.color.system_neutral2_800,
+ android.R.color.system_neutral2_900,
+ android.R.color.system_neutral2_1000,
+ )
+ }
+}
diff --git a/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt b/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
index eec7d5a..b9cc997 100644
--- a/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
+++ b/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
@@ -16,6 +16,7 @@
package com.android.wallpaper.picker.common.preview.ui.binder
+import android.app.WallpaperManager
import android.os.Bundle
import android.os.Message
import androidx.core.os.bundleOf
@@ -23,6 +24,9 @@
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.customization.model.color.ColorOptionsProvider
+import com.android.customization.picker.color.data.util.MaterialColorsGenerator
+import com.android.systemui.monet.ColorScheme
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID
@@ -44,8 +48,11 @@
@Singleton
class ThemePickerWorkspaceCallbackBinder
@Inject
-constructor(private val defaultWorkspaceCallbackBinder: DefaultWorkspaceCallbackBinder) :
- WorkspaceCallbackBinder {
+constructor(
+ private val wallpaperManager: WallpaperManager,
+ private val defaultWorkspaceCallbackBinder: DefaultWorkspaceCallbackBinder,
+ private val materialColorsGenerator: MaterialColorsGenerator,
+) : WorkspaceCallbackBinder {
override fun bind(
workspaceCallback: Message,
@@ -81,7 +88,7 @@
KEY_INITIALLY_SELECTED_SLOT_ID,
SLOT_ID_BOTTOM_START,
)
- }
+ },
)
else ->
workspaceCallback.sendMessage(
@@ -135,7 +142,32 @@
viewModel.shapeAndGridPickerViewModel.previewingGridOptionKey.collect {
workspaceCallback.sendMessage(
MESSAGE_ID_UPDATE_GRID,
- bundleOf(KEY_GRID_NAME to it)
+ bundleOf(KEY_GRID_NAME to it),
+ )
+ }
+ }
+
+ launch {
+ viewModel.colorPickerViewModel2.previewingColorOption.collect {
+ if (it == null) {
+ workspaceCallback.sendMessage(MESSAGE_ID_UPDATE_COLOR, Bundle())
+ return@collect
+ }
+ val seedColor =
+ it.colorOption.seedColor
+ ?: getSeedColorFromSource(it.colorOption.source)
+ ?: return@collect
+ val (ids, colors) =
+ materialColorsGenerator.generate(
+ seedColor,
+ it.colorOption.style,
+ )
+ workspaceCallback.sendMessage(
+ MESSAGE_ID_UPDATE_COLOR,
+ Bundle().apply {
+ putIntArray(KEY_COLOR_RESOURCE_IDS, ids)
+ putIntArray(KEY_COLOR_VALUES, colors)
+ },
)
}
}
@@ -144,8 +176,22 @@
}
}
+ private fun getSeedColorFromSource(source: String?): Int? {
+ return when (source) {
+ ColorOptionsProvider.COLOR_SOURCE_HOME -> WallpaperManager.FLAG_SYSTEM
+ ColorOptionsProvider.COLOR_SOURCE_LOCK -> WallpaperManager.FLAG_LOCK
+ else -> null
+ }
+ ?.let { wallpaperManager.getWallpaperColors(it) }
+ ?.let { ColorScheme.getSeedColor(it) }
+ }
+
companion object {
const val MESSAGE_ID_UPDATE_GRID = 7414
const val KEY_GRID_NAME = "grid_name"
+
+ const val MESSAGE_ID_UPDATE_COLOR = 856
+ const val KEY_COLOR_RESOURCE_IDS: String = "color_resource_ids"
+ const val KEY_COLOR_VALUES: String = "color_values"
}
}