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

Commit 8a49bc6c authored by Catherine Liang's avatar Catherine Liang
Browse files

Send messages to Launcher preview when previewing color

When the user selects a color in the color picker, generate a list of
color ids and another list of their corresponding color overrides, and
send messages to the Launcher preview to update accordingly.

Color generation was previously done in the Launcher
MaterialColorsGenerator, but now moved to the picker.

Flag: com.android.systemui.shared.new_customization_picker_ui
Test: manually verified that colors are previewed and not applied
Bug: 350718581
Change-Id: I839afd886e23df4d505517a5b7a60108855fa01c
parent b1fee73c
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -102,6 +102,23 @@ public abstract class ColorOption implements CustomizationOption<ColorOption> {
        }
    }

    /**
     * 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.
     *
+191 −0
Original line number Diff line number Diff line
/*
 * 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,
            )
    }
}
+50 −4
Original line number Diff line number Diff line
@@ -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.Lifecycle
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 @@ import kotlinx.coroutines.launch
@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 @@ constructor(private val defaultWorkspaceCallbackBinder: DefaultWorkspaceCallback
                                                    KEY_INITIALLY_SELECTED_SLOT_ID,
                                                    SLOT_ID_BOTTOM_START,
                                                )
                                            }
                                            },
                                        )
                                    else ->
                                        workspaceCallback.sendMessage(
@@ -135,7 +142,32 @@ constructor(private val defaultWorkspaceCallbackBinder: DefaultWorkspaceCallback
                            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 @@ constructor(private val defaultWorkspaceCallbackBinder: DefaultWorkspaceCallback
        }
    }

    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"
    }
}