diff --git a/ThemePickerGoogle/Android.bp b/ThemePickerGoogle/Android.bp
index b80f1e8..c479c92 100644
--- a/ThemePickerGoogle/Android.bp
+++ b/ThemePickerGoogle/Android.bp
@@ -46,13 +46,11 @@ genrule {
+ "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR"
}
-//
-// Build app code.
-//
-android_app {
- name: "ThemePickerGoogle",
+android_library {
+ name: "ThemePickerGoogle_core",
static_libs: [
+ "monet",
"wallpaper-common-deps",
"SettingsLibSettingsTheme",
"SystemUIFlagsLib",
@@ -60,6 +58,27 @@ android_app {
"styleprotoslite",
],
+ optimize: {
+ enabled: false,
+ },
+
+ resource_dirs: ["res"],
+
+ resource_zips: [":WallpaperPicker2_res", ":ThemePicker_res"],
+
+ kotlincflags: ["-Xjvm-default=enable"],
+}
+
+//
+// Build app code.
+//
+android_app {
+ name: "ThemePickerGoogle",
+
+ static_libs: [
+ "ThemePickerGoogle_core",
+ ],
+
srcs: [
":WallpaperPicker2_srcs",
":ThemePicker_srcs",
diff --git a/ThemePickerGoogle/AndroidManifest.xml b/ThemePickerGoogle/AndroidManifest.xml
index 82cbcb4..e14b200 100644
--- a/ThemePickerGoogle/AndroidManifest.xml
+++ b/ThemePickerGoogle/AndroidManifest.xml
@@ -5,6 +5,8 @@
+
+
+
+
+
+
diff --git a/ThemePickerGoogle/res/drawable/color_chip_seed_filled0.xml b/ThemePickerGoogle/res/drawable/color_chip_seed_filled0.xml
new file mode 100644
index 0000000..ebce662
--- /dev/null
+++ b/ThemePickerGoogle/res/drawable/color_chip_seed_filled0.xml
@@ -0,0 +1,10 @@
+
+
+ -
+
+
+
+
+
+
+
diff --git a/ThemePickerGoogle/res/drawable/color_chip_seed_filled1.xml b/ThemePickerGoogle/res/drawable/color_chip_seed_filled1.xml
new file mode 100644
index 0000000..3abff07
--- /dev/null
+++ b/ThemePickerGoogle/res/drawable/color_chip_seed_filled1.xml
@@ -0,0 +1,10 @@
+
+
+ -
+
+
+
+
+
+
+
diff --git a/ThemePickerGoogle/res/drawable/color_chip_seed_filled2.xml b/ThemePickerGoogle/res/drawable/color_chip_seed_filled2.xml
new file mode 100644
index 0000000..4f11270
--- /dev/null
+++ b/ThemePickerGoogle/res/drawable/color_chip_seed_filled2.xml
@@ -0,0 +1,10 @@
+
+
+ -
+
+
+
+
+
+
+
diff --git a/ThemePickerGoogle/res/drawable/color_chip_seed_filled3.xml b/ThemePickerGoogle/res/drawable/color_chip_seed_filled3.xml
new file mode 100644
index 0000000..54401aa
--- /dev/null
+++ b/ThemePickerGoogle/res/drawable/color_chip_seed_filled3.xml
@@ -0,0 +1,10 @@
+
+
+ -
+
+
+
+
+
+
+
diff --git a/ThemePickerGoogle/res/layout/color_option.xml b/ThemePickerGoogle/res/layout/color_option.xml
new file mode 100644
index 0000000..e936210
--- /dev/null
+++ b/ThemePickerGoogle/res/layout/color_option.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/ThemePickerGoogle/res/layout/color_options_view.xml b/ThemePickerGoogle/res/layout/color_options_view.xml
new file mode 100644
index 0000000..b86428f
--- /dev/null
+++ b/ThemePickerGoogle/res/layout/color_options_view.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/ThemePickerGoogle/res/layout/color_section_view.xml b/ThemePickerGoogle/res/layout/color_section_view.xml
new file mode 100644
index 0000000..dd86748
--- /dev/null
+++ b/ThemePickerGoogle/res/layout/color_section_view.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ThemePickerGoogle/res/layout/color_seed_option.xml b/ThemePickerGoogle/res/layout/color_seed_option.xml
new file mode 100644
index 0000000..8f16c8c
--- /dev/null
+++ b/ThemePickerGoogle/res/layout/color_seed_option.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ThemePickerGoogle/res/values/dimens.xml b/ThemePickerGoogle/res/values/dimens.xml
new file mode 100644
index 0000000..ae3e43b
--- /dev/null
+++ b/ThemePickerGoogle/res/values/dimens.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ 2dp
+ 11dp
+ 96dp
+ 24dp
+ 12dp
+ 10dp
+ 6dp
+ 48dp
+ 48dp
+ 24dp
+ 32dp
+ 82dp
+
+ 20dp
+
diff --git a/ThemePickerGoogle/res/values/strings.xml b/ThemePickerGoogle/res/values/strings.xml
index 9aaef39..e66af09 100644
--- a/ThemePickerGoogle/res/values/strings.xml
+++ b/ThemePickerGoogle/res/values/strings.xml
@@ -18,4 +18,9 @@
Styles
+
+ Wallpaper colors
+ Wallpaper color
+ Basic colors
+ Color changed
diff --git a/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorBundle.kt b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorBundle.kt
new file mode 100644
index 0000000..65e1f9c
--- /dev/null
+++ b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorBundle.kt
@@ -0,0 +1,59 @@
+package com.google.android.customization.model.color
+
+import android.annotation.SuppressLint
+import android.content.res.ColorStateList
+import android.content.res.Configuration
+import android.graphics.drawable.GradientDrawable
+import android.view.View
+import android.widget.ImageView
+import com.android.wallpaper.R
+
+class ColorBundle(
+ title: String?,
+ map: Map?,
+ isDefault: Boolean,
+ index: Int,
+ private val mPreviewInfo: PreviewInfo
+) : ColorOption(
+ title!!, map!!, isDefault, index
+) {
+ class PreviewInfo(
+ val secondaryColorLight: Int,
+ val secondaryColorDark: Int,
+ )
+
+ @SuppressLint("UseCompatLoadingForDrawables")
+ override fun bindThumbnailTile(view: View) {
+ val resources = view.context.resources
+ val thumbnailView = view.findViewById(R.id.color_preview_icon)
+ val secondaryColor =
+ if ((resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES) mPreviewInfo.secondaryColorDark
+ else mPreviewInfo.secondaryColorLight
+ val gradientDrawable = view.resources.getDrawable(
+ R.drawable.color_chip_medium_filled, thumbnailView.context.theme
+ ) as GradientDrawable
+ if (secondaryColor != 0) {
+ gradientDrawable.setTintList(ColorStateList.valueOf(secondaryColor))
+ } else {
+ gradientDrawable.setTintList(ColorStateList.valueOf(resources.getColor(R.color.material_white_100)))
+ }
+ thumbnailView.setImageDrawable(gradientDrawable)
+ val context = view.context
+ if (mContentDescription == null) {
+ val string = context.getString(R.string.default_theme_title)
+ mContentDescription = if (mIsDefault) {
+ string
+ } else {
+ mTitle
+ }
+ }
+ view.contentDescription = mContentDescription
+ }
+
+ override fun getLayoutResId(): Int {
+ return R.layout.color_option
+ }
+
+ override val source: String
+ get() = "preset"
+}
diff --git a/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorCustomizationManager.kt b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorCustomizationManager.kt
new file mode 100644
index 0000000..ead3d95
--- /dev/null
+++ b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorCustomizationManager.kt
@@ -0,0 +1,238 @@
+package com.google.android.customization.model.color
+
+import android.app.WallpaperColors
+import android.content.ContentResolver
+import android.content.Context
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.provider.Settings
+import android.text.TextUtils
+import android.util.Log
+import com.android.wallpaper.R
+import com.android.customization.model.CustomizationManager
+import com.android.customization.model.CustomizationManager.OptionsFetchedListener
+import com.google.android.customization.picker.color.ColorSectionView
+import org.json.JSONException
+import org.json.JSONObject
+import java.util.concurrent.Executors
+
+class ColorCustomizationManager(
+ val mProvider: ColorOptionsProvider,
+ private val mContentResolver: ContentResolver
+) : CustomizationManager {
+ var mCurrentOverlays: Map? = null
+ private var mCurrentSource: String? = null
+ var mHomeWallpaperColors: WallpaperColors? = null
+ var mLockWallpaperColors: WallpaperColors? = null
+
+ companion object {
+ var COLOR_OVERLAY_SETTINGS: Set? = null
+ private var sColorCustomizationManager: ColorCustomizationManager? = null
+ val sExecutorService = Executors.newSingleThreadExecutor()!!
+ fun getInstance(
+ context: Context
+ ): ColorCustomizationManager? {
+ if (sColorCustomizationManager == null) {
+ val applicationContext = context.applicationContext
+ sColorCustomizationManager = ColorCustomizationManager(
+ ColorProvider(
+ applicationContext, applicationContext.getString(
+ R.string.themes_stub_package
+ )
+ ), applicationContext.contentResolver
+ )
+ }
+ return sColorCustomizationManager
+ }
+
+ init {
+ val hashSet = HashSet()
+ COLOR_OVERLAY_SETTINGS = hashSet
+ hashSet.add("android.theme.customization.system_palette")
+ hashSet.add("android.theme.customization.accent_color")
+ hashSet.add("android.theme.customization.color_source")
+ }
+ }
+
+ init {
+ mContentResolver.registerContentObserver(Settings.Secure.CONTENT_URI, true, object :
+ ContentObserver(Handler(Looper.getMainLooper())) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ super.onChange(selfChange, uri)
+ if (TextUtils.equals(
+ uri!!.lastPathSegment,
+ "theme_customization_overlay_packages"
+ )
+ ) {
+ mCurrentOverlays = null
+ }
+
+ }
+ })
+ }
+
+ val currentColorSource: String?
+ get() {
+ if (mCurrentSource == null) {
+ parseSettings(storedOverlays)
+ }
+ return mCurrentSource
+ }
+ val storedOverlays: String?
+ get() = Settings.Secure.getString(mContentResolver, "theme_customization_overlay_packages")
+
+ fun parseSettings(str: String?) {
+ val hashMap = HashMap()
+ if (str != null) {
+ try {
+ val jSONObject = JSONObject(str)
+ val names = jSONObject.names()
+ if (names != null) {
+ for (i in 0 until names.length()) {
+ val string = names.getString(i)
+ if ((COLOR_OVERLAY_SETTINGS as HashSet?)!!.contains(string)) {
+ try {
+ hashMap[string] = jSONObject.getString(string)
+ } catch (e: JSONException) {
+ Log.e(
+ "ColorCustomizationManager",
+ "parseColorOverlays: " + e.localizedMessage,
+ e
+ )
+ }
+ }
+ }
+ }
+ } catch (e2: JSONException) {
+ Log.e("ColorCustomizationManager", e2.localizedMessage!!)
+ }
+ }
+ mCurrentSource = hashMap.remove("android.theme.customization.color_source")
+ mCurrentOverlays = hashMap
+ }
+
+ override fun isAvailable(): Boolean {
+ return true
+ }
+
+ fun setThemeBundle(colorSectionController: ColorSectionController, option: ColorOption) {
+ if (SystemClock.elapsedRealtime() - colorSectionController.mLastColorApplyingTime >= 500) {
+ colorSectionController.mLastColorApplyingTime = SystemClock.elapsedRealtime()
+ val callback: CustomizationManager.Callback = object : CustomizationManager.Callback {
+ override fun onError(th2: Throwable?) {
+ Log.w("ColorSectionController", "Apply theme with error: null")
+ }
+
+ override fun onSuccess() {
+ val colorSectionView: ColorSectionView =
+ colorSectionController.mColorSectionView!!
+ colorSectionView.announceForAccessibility(
+ colorSectionView.context.getString(
+ R.string.color_changed
+ )
+ )
+ val wallpaperColors = colorSectionController.mLockWallpaperColors
+ var i3 = 0
+ val z2 =
+ wallpaperColors == null || wallpaperColors == colorSectionController.mHomeWallpaperColors
+ if (TextUtils.equals(option.source, "preset")) {
+ i3 = 26
+ } else if (z2) {
+ i3 = 25
+ } else {
+ val source = option.source
+ if (source == "lock_wallpaper") {
+ i3 = 24
+ } else if (source == "home_wallpaper") {
+ i3 = 23
+ }
+ }
+ colorSectionController.mEventLogger.logColorApplied(i3, option.mIndex)
+ }
+ }
+ sExecutorService.submit {
+ applyBundle(option, callback)
+ }
+ return
+ }
+ }
+
+ private fun applyBundle(option: ColorOption, callback: CustomizationManager.Callback) {
+ var mStoredOverlays = storedOverlays
+ if (TextUtils.isEmpty(mStoredOverlays) || mStoredOverlays == null) {
+ mStoredOverlays = "{}"
+ }
+ var z4: Boolean
+ var jSONObject: JSONObject? = null
+ try {
+ jSONObject = JSONObject(mStoredOverlays)
+ try {
+ val jsonPackages: JSONObject = option.getJsonPackages(true)
+ val it: Iterator<*> =
+ (COLOR_OVERLAY_SETTINGS as HashSet?)!!.iterator()
+ while (it.hasNext()) {
+ jSONObject.remove(it.next() as String?)
+ }
+ val keys: Iterator = jsonPackages.keys()
+ while (keys.hasNext()) {
+ val next = keys.next()
+ jSONObject.put(next, jsonPackages.get(next))
+ }
+ jSONObject.put(
+ "android.theme.customization.color_source",
+ option.source
+ )
+ jSONObject.put(
+ "android.theme.customization.color_index",
+ option.mIndex.toString()
+ )
+ if ("preset" != option.source) {
+ val wallpaperColors = mLockWallpaperColors
+ if (wallpaperColors != null && wallpaperColors != mHomeWallpaperColors) {
+ z4 = false
+ jSONObject.put(
+ "android.theme.customization.color_both",
+ if (!z4) "1" else "0"
+ )
+ }
+ z4 = true
+ jSONObject.put(
+ "android.theme.customization.color_both",
+ if (!z4) "1" else "0"
+ )
+ } else {
+ jSONObject.remove("android.theme.customization.color_both")
+ }
+ } catch (e2: JSONException) {
+ e2.printStackTrace()
+ Handler(Looper.getMainLooper()).post {
+ val success = Settings.Secure.putString(
+ mContentResolver,
+ "theme_customization_overlay_packages", jSONObject.toString()
+ )
+ if (success) callback.onSuccess()
+ else callback.onError(null)
+ }
+ return
+ }
+ } catch (e3: JSONException) {
+ e3.printStackTrace()
+ }
+ Handler(Looper.getMainLooper()).post {
+ val success = jSONObject != null && Settings.Secure.putString(
+ mContentResolver,
+ "theme_customization_overlay_packages", jSONObject.toString()
+ )
+ if (success) callback.onSuccess()
+ else callback.onError(null)
+ }
+ }
+
+ override fun apply(option: ColorOption, callback: CustomizationManager.Callback) {
+ applyBundle(option, callback)
+ }
+ override fun fetchOptions(callback: OptionsFetchedListener, reload: Boolean) {}
+}
diff --git a/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorOption.kt b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorOption.kt
new file mode 100644
index 0000000..cfc7d94
--- /dev/null
+++ b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorOption.kt
@@ -0,0 +1,97 @@
+package com.google.android.customization.model.color
+
+import android.text.TextUtils
+import android.util.Log
+import com.android.customization.model.CustomizationManager
+import com.android.customization.model.CustomizationOption
+import org.json.JSONException
+import org.json.JSONObject
+import java.util.*
+import java.util.stream.Collectors
+
+abstract class ColorOption(
+ val mTitle: String,
+ map: Map,
+ val mIsDefault: Boolean,
+ val mIndex: Int
+) : CustomizationOption {
+ var mContentDescription: CharSequence? = null
+ private val mPackagesByCategory: Map
+
+ fun getJsonPackages(z: Boolean): JSONObject {
+ val jSONObject: JSONObject = if (mIsDefault) {
+ JSONObject()
+ } else {
+ val jSONObject2 = JSONObject(mPackagesByCategory)
+ val keys = jSONObject2.keys()
+ val hashSet: HashSet = HashSet()
+ while (keys.hasNext()) {
+ val next = keys.next()
+ if (jSONObject2.isNull(next)) {
+ hashSet.add(next)
+ }
+ }
+ val it: Iterator<*> = hashSet.iterator()
+ while (it.hasNext()) {
+ jSONObject2.remove(it.next() as String?)
+ }
+ jSONObject2
+ }
+ if (z) {
+ try {
+ jSONObject.put(TIMESTAMP_FIELD, System.currentTimeMillis())
+ } catch (unused: JSONException) {
+ Log.e("ColorOption", "Couldn't add timestamp to serialized themebundle")
+ }
+ }
+ return jSONObject
+ }
+
+ abstract val source: String
+
+ override fun getTitle(): String {
+ return mTitle
+ }
+
+ override fun isActive(customizationManager: CustomizationManager): Boolean {
+ val colorCustomizationManager = customizationManager as ColorCustomizationManager
+ if (mIsDefault) {
+ val storedOverlays = colorCustomizationManager.storedOverlays
+ if (!TextUtils.isEmpty(storedOverlays) && "{}" != storedOverlays) {
+ if (colorCustomizationManager.mCurrentOverlays == null) {
+ colorCustomizationManager.parseSettings(colorCustomizationManager.storedOverlays)
+ }
+ if (colorCustomizationManager.mCurrentOverlays!!.isNotEmpty() &&
+ (storedOverlays!!.contains("android.theme.customization.system_palette") ||
+ storedOverlays.contains("android.theme.customization.accent_color"))
+ ) {
+ return false
+ }
+ }
+ return true
+ }
+ if (colorCustomizationManager.mCurrentOverlays == null) {
+ colorCustomizationManager.parseSettings(colorCustomizationManager.storedOverlays)
+ }
+ val map = colorCustomizationManager.mCurrentOverlays
+ val currentColorSource = colorCustomizationManager.currentColorSource
+ return (TextUtils.isEmpty(currentColorSource) || source == currentColorSource) && mPackagesByCategory == map
+ }
+
+ companion object {
+ const val TIMESTAMP_FIELD = "_applied_timestamp"
+ }
+
+ init {
+ mPackagesByCategory = Collections.unmodifiableMap(
+ map.entries.stream().filter { t -> t.value != null }
+ .collect(
+ Collectors.toMap(
+ { t -> t.key },
+ { t -> t.value }
+ )
+ ) as Map
+ )
+
+ }
+}
diff --git a/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorOptionsProvider.kt b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorOptionsProvider.kt
new file mode 100644
index 0000000..8191ab7
--- /dev/null
+++ b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorOptionsProvider.kt
@@ -0,0 +1,3 @@
+package com.google.android.customization.model.color
+
+interface ColorOptionsProvider
diff --git a/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorProvider.kt b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorProvider.kt
new file mode 100644
index 0000000..4327bde
--- /dev/null
+++ b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorProvider.kt
@@ -0,0 +1,241 @@
+package com.google.android.customization.model.color
+
+import android.app.WallpaperColors
+import android.content.Context
+import android.content.res.ColorStateList
+import android.util.Log
+import androidx.core.graphics.ColorUtils
+import com.android.customization.model.ResourcesApkProvider
+import com.android.wallpaper.compat.WallpaperManagerCompat
+import com.android.wallpaper.module.InjectorProvider
+import com.android.systemui.monet.ColorScheme
+import com.google.android.customization.model.color.ColorUtils.toColorString
+import kotlinx.coroutines.CoroutineScope
+import java.util.*
+import kotlin.collections.ArrayList
+import kotlin.collections.HashMap
+
+class ColorProvider(context: Context, stubPackageName: String) :
+ ResourcesApkProvider(context, stubPackageName), ColorOptionsProvider {
+ var colorBundles: List? = null
+ var homeWallpaperColors: WallpaperColors? = null
+ var lockWallpaperColors: WallpaperColors? = null
+ val scope: CoroutineScope? = null
+ private fun buildBundle(
+ seed: Int,
+ index: Int,
+ mIsDefault: Boolean,
+ source: String?,
+ list: ArrayList
+ ) {
+
+ val hashMap: HashMap = HashMap()
+ val colorScheme = ColorScheme(seed, false)
+ val colorSchemeDark = ColorScheme(seed, true)
+ val secondaryColorLight = intArrayOf(
+ ColorUtils.setAlphaComponent(colorScheme.accent1[2], 255), ColorUtils.setAlphaComponent(
+ colorScheme.accent1[2], 255
+ ), ColorStateList.valueOf(
+ colorScheme.accent3[6]
+ ).withLStar(85.0f).colors[0], ColorUtils.setAlphaComponent(
+ colorScheme.accent1[6], 255
+ )
+ )
+ val secondaryColorDark = intArrayOf(
+ ColorUtils.setAlphaComponent(colorSchemeDark.accent1[2], 255),
+ ColorUtils.setAlphaComponent(
+ colorSchemeDark.accent1[2], 255
+ ),
+ ColorStateList.valueOf(
+ colorSchemeDark.accent3[6]
+ ).withLStar(85.0f).colors[0],
+ ColorUtils.setAlphaComponent(
+ colorSchemeDark.accent1[6], 255
+ )
+ )
+ var source3 = ""
+ val source2 = if (mIsDefault) {
+ source3
+ } else {
+ toColorString(seed)
+ }
+ hashMap["android.theme.customization.system_palette"] = source2
+ if (!mIsDefault) {
+ source3 = toColorString(seed)
+ }
+ hashMap["android.theme.customization.accent_color"] = source3
+ list.add(
+ ColorSeedOption(
+ source,
+ hashMap,
+ mIsDefault,
+ source!!,
+ 1 + index,
+ ColorSeedOption.PreviewInfo(secondaryColorLight, secondaryColorDark)
+ )
+ )
+ }
+
+ private fun loadPreset() {
+ val bundlesList = ArrayList()
+ val bundleNames = mStubApkResources.getStringArray(
+ mStubApkResources.getIdentifier(
+ "color_bundles",
+ "array",
+ mStubPackageName
+ )
+ )
+ for (i in bundleNames.indices) {
+ if (i == 4) break
+ bundlesList.add(bundleNames[i])
+ }
+ val colorPresetBundles = ArrayList()
+ var position = 1;
+ for (bundle in bundlesList) {
+ val hashMap: HashMap = HashMap()
+ val bundleName = getItemStringFromStub("bundle_name_", bundle)
+ val bundleColorPrimary = getItemColorFromStub("color_primary_", bundle)
+ val bundleColorSecondary = getItemColorFromStub("color_secondary_", bundle)
+ hashMap["android.theme.customization.system_palette"] =
+ toColorString(bundleColorSecondary)
+ hashMap["android.theme.customization.accent_color"] = toColorString(bundleColorPrimary)
+ val accentColor = ColorScheme(bundleColorPrimary, false).accentColor
+ val accentColor2 = ColorScheme(bundleColorPrimary, true).accentColor
+ colorPresetBundles.add(
+ ColorBundle(
+ bundleName,
+ hashMap,
+ false,
+ index = position,
+ mPreviewInfo = ColorBundle.PreviewInfo(
+ accentColor,
+ accentColor2
+ )
+ )
+ )
+ position++
+ }
+ this.colorBundles = colorPresetBundles
+ }
+
+ fun buildColorSeeds(
+ wallpaperColors: WallpaperColors,
+ count: Int,
+ source: String?,
+ isDefault: Boolean,
+ list: ArrayList
+ ) {
+ val list2: List
+ val list3: List
+ val seedColors: List = ColorScheme.getSeedColors(wallpaperColors)
+ loadPreset()
+ buildBundle(
+ seedColors[0],
+ 0,
+ isDefault,
+ source,
+ list
+ )
+ val size = seedColors.size - 1
+ list2 = if (size <= 0) {
+ ArrayList()
+ } else if (size != 1) {
+ val arrayList: ArrayList = ArrayList(size)
+ val listIterator = seedColors.listIterator(1)
+ while (listIterator.hasNext()) {
+ arrayList.add(listIterator.next())
+ }
+ arrayList
+ } else if (seedColors.isNotEmpty()) {
+ listOf(seedColors[seedColors.size - 1])
+ } else {
+ throw NoSuchElementException("List is empty.")
+ }
+ val i3 = count - 1
+ var index4 = 0
+ if (i3 >= 0) {
+ list3 = if (i3 == 0) {
+ ArrayList()
+ } else (if (i3 >= list2.size) {
+ list2
+ } else if (i3 == 1) {
+ listOf(list2[0])
+ } else {
+ val arrayList2: ArrayList = ArrayList(i3)
+ var i5 = 0
+ for (obj in list2) {
+ arrayList2.add(obj)
+ i5++
+ if (i5 == i3) {
+ break
+ }
+ }
+ arrayList2
+ })
+ for (seed in list3) {
+ index4++
+ buildBundle(seed, index4, false, source, list)
+ }
+ return
+ }
+ throw IllegalArgumentException("Requested element count $i3 is less than zero.")
+ }
+
+ companion object {
+
+ fun loadSeedColors(
+ colorProvider: ColorProvider,
+ wallpaperColors: WallpaperColors?,
+ wallpaperColors2: WallpaperColors?
+ ) {
+ val arrayList = ArrayList()
+ if (wallpaperColors != null) {
+ val count = if (wallpaperColors2 == null) 4 else 2
+ if (wallpaperColors2 != null) {
+ val wallpaperManagerCompat: WallpaperManagerCompat =
+ InjectorProvider.getInjector()
+ .getWallpaperManagerCompat(colorProvider.mContext)
+ var isDefault = true
+ if (wallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK) <= wallpaperManagerCompat.getWallpaperId(
+ WallpaperManagerCompat.FLAG_SYSTEM
+ )
+ ) {
+ isDefault = false
+ }
+ colorProvider.buildColorSeeds(
+ if (isDefault) wallpaperColors2 else wallpaperColors,
+ count,
+ if (isDefault) "lock_wallpaper" else "home_wallpaper",
+ true,
+ arrayList
+ )
+ colorProvider.buildColorSeeds(
+ if (isDefault) wallpaperColors else wallpaperColors2,
+ count,
+ if (isDefault) "home_wallpaper" else "lock_wallpaper",
+ false,
+ arrayList
+ )
+ } else {
+ colorProvider.buildColorSeeds(
+ wallpaperColors,
+ count,
+ "home_wallpaper",
+ true,
+ arrayList
+ )
+ }
+ val list = colorProvider.colorBundles
+ val arrayList2 = ArrayList()
+ if (list != null) {
+ for (t in list) {
+ arrayList2.add(t)
+ }
+ }
+ arrayList.addAll(arrayList2)
+ colorProvider.colorBundles = arrayList
+ }
+ }
+ }
+
+}
diff --git a/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorSectionController.kt b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorSectionController.kt
new file mode 100644
index 0000000..f139ab0
--- /dev/null
+++ b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorSectionController.kt
@@ -0,0 +1,260 @@
+package com.google.android.customization.model.color
+
+import android.app.Activity
+import android.app.WallpaperColors
+import android.content.Context
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewpager2.widget.ViewPager2
+import com.android.customization.model.CustomizationManager
+import com.android.customization.module.CustomizationInjector
+import com.android.customization.module.ThemesUserEventLogger
+import com.android.customization.widget.OptionSelectorController
+import com.android.wallpaper.R
+import com.android.wallpaper.model.CustomizationSectionController;
+import com.android.wallpaper.model.CustomizationSectionController.CustomizationSectionNavigationController;
+import com.android.wallpaper.module.InjectorProvider
+import com.android.wallpaper.widget.SeparatedTabLayout
+import com.android.wallpaper.model.WallpaperColorsViewModel
+import com.google.android.customization.picker.color.ColorSectionView
+import kotlinx.coroutines.launch
+
+
+class ColorSectionController(
+ activity: Activity?,
+ wallpaperColorsViewModel: WallpaperColorsViewModel,
+ lifecycleOwner: LifecycleOwner,
+ bundle: Bundle?,
+ private val navigationController: CustomizationSectionNavigationController
+) : CustomizationSectionController {
+
+ val mColorManager: ColorCustomizationManager
+ var mColorSectionView: ColorSectionView? = null
+ private var mColorSectionAdapter = ColorSectionAdapter()
+ private lateinit var mColorViewPager: ViewPager2
+ val mEventLogger: ThemesUserEventLogger
+ var mHomeWallpaperColors: WallpaperColors? = null
+ private var mHomeWallpaperColorsReady = false
+ private val mLifecycleOwner: LifecycleOwner
+ var mLockWallpaperColors: WallpaperColors? = null
+ private var mLockWallpaperColorsReady = false
+ val mPresetColorOptions: MutableList = ArrayList()
+ var mSelectedColor: ColorOption? = null
+ private var mTabLayout: SeparatedTabLayout? = null
+ private var mTabPositionToRestore: Int? = null
+ val mWallpaperColorOptions: MutableList = ArrayList()
+ private val mWallpaperColorsViewModel: WallpaperColorsViewModel
+ var mLastColorApplyingTime: Long = 0
+
+
+ inner class ColorSectionAdapter :
+ RecyclerView.Adapter() {
+ private val mItemCounts = 2
+
+ inner class ColorOptionsViewHolder(view: View) :
+ RecyclerView.ViewHolder(
+ view
+ )
+
+ override fun getItemCount(): Int {
+ return mItemCounts
+ }
+
+ override fun getItemViewType(i: Int): Int {
+ return R.layout.color_options_view
+ }
+
+ override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
+ val view = viewHolder.itemView as? RecyclerView ?: return
+ val colorSectionController = this@ColorSectionController
+ val colorOptions =
+ if (position == 0) colorSectionController.mWallpaperColorOptions
+ else colorSectionController.mPresetColorOptions
+ val optionSelectorController = OptionSelectorController(
+ view,
+ colorOptions,
+ true,
+ OptionSelectorController.CheckmarkStyle.CENTER
+ )
+ optionSelectorController.initOptions(colorSectionController.mColorManager)
+ colorSectionController.setUpColorOptionsController(optionSelectorController)
+ }
+
+ override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): RecyclerView.ViewHolder {
+ return ColorOptionsViewHolder(
+ LayoutInflater.from(viewGroup.context).inflate(i, viewGroup, false)
+ )
+ }
+ }
+
+ override fun createView(context: Context): ColorSectionView {
+ mColorSectionView = LayoutInflater.from(context)
+ .inflate(R.layout.color_section_view, null as ViewGroup?) as ColorSectionView
+ mColorViewPager = mColorSectionView!!.requireViewById(R.id.color_view_pager)
+ mTabLayout = mColorSectionView!!.requireViewById(R.id.separated_tabs)
+ if (mColorViewPager.adapter == null) {
+ mColorViewPager.setAdapter(mColorSectionAdapter)
+ }
+ mTabLayout!!.setViewPager(mColorViewPager)
+ mWallpaperColorsViewModel.homeWallpaperColors.observe(mLifecycleOwner) {
+ mHomeWallpaperColors = it
+ mHomeWallpaperColorsReady = true
+ maybeLoadColors()
+ }
+
+ mWallpaperColorsViewModel.lockWallpaperColors.observe(mLifecycleOwner) {
+ mLockWallpaperColors = it
+ mLockWallpaperColorsReady = true
+ maybeLoadColors()
+ }
+
+ mLifecycleOwner.lifecycleScope.launchWhenResumed {
+ mTabPositionToRestore?.let { mColorViewPager.setCurrentItem(it, false) }
+ }
+
+ return mColorSectionView!!
+ }
+
+ override fun isAvailable(context: Context?): Boolean {
+ return context != null && ColorUtils.isMonetEnabled(context)
+ }
+
+ private fun maybeLoadColors() {
+ if (mHomeWallpaperColorsReady && mLockWallpaperColorsReady) {
+ val wallpaperColors = mHomeWallpaperColors
+ var wallpaperColors2 = mLockWallpaperColors
+ mColorManager.mHomeWallpaperColors = wallpaperColors
+ mColorManager.mLockWallpaperColors = wallpaperColors2
+ val optionsFetcher =
+ object : CustomizationManager.OptionsFetchedListener {
+ override fun onError(th: Throwable?) {
+ if (th != null) {
+ Log.e("ColorSectionController", "Error loading theme bundles", th)
+ }
+ }
+
+ override fun onOptionsLoaded(list: List) {
+ if (list.isNotEmpty()) {
+ var colorOption: ColorOption?
+ val colorOption2: ColorOption
+ mWallpaperColorOptions.clear()
+ mPresetColorOptions.clear()
+ for (colorOption3 in list) {
+ if (colorOption3 is ColorSeedOption) {
+ mWallpaperColorOptions.add(colorOption3)
+ } else if (colorOption3 is ColorBundle) {
+ mPresetColorOptions.add(colorOption3)
+ }
+ }
+ val allColors = ArrayList()
+ allColors.addAll(mWallpaperColorOptions)
+ allColors.addAll(mPresetColorOptions)
+ val iterator = allColors.iterator()
+ while (true) {
+ if (!iterator.hasNext()) {
+ colorOption = null
+ break
+ }
+ colorOption = iterator.next()
+ if (colorOption!!.isActive(mColorManager)) {
+ break
+ }
+ }
+ if (colorOption == null) {
+ colorOption2 =
+ (if (mWallpaperColorOptions.isEmpty()) mPresetColorOptions[0]!! else mWallpaperColorOptions[0]!!)
+ colorOption = colorOption2
+ }
+ mSelectedColor = colorOption
+ mColorViewPager.post {
+ mColorViewPager.adapter?.notifyItemChanged(0)
+ if (mTabLayout != null && mTabLayout!!.tabCount == 0) {
+ val newTab = mTabLayout!!.newTab()
+ newTab.setText(R.string.wallpaper_color_tab)
+ mTabLayout!!.addTab(newTab, 0, mTabLayout!!.tabCount == 0)
+ val newTab2 = mTabLayout!!.newTab()
+ newTab2.setText(R.string.preset_color_tab)
+ mTabLayout!!.addTab(newTab2, 1, mTabLayout!!.tabCount == 0)
+ }
+ if (mWallpaperColorOptions.isEmpty()) {
+ mTabLayout!!.getTabAt(0)!!.view.isEnabled = false
+ mColorViewPager.setCurrentItem(1, false)
+ }
+ mColorViewPager.setCurrentItem(
+ if ("preset" == mColorManager.currentColorSource) 1 else 0,
+ false
+ )
+ }
+ }
+ }
+ }
+ if (wallpaperColors2 != null && wallpaperColors2 == wallpaperColors) {
+ wallpaperColors2 = null
+ }
+ val wallpaperColors3 = mColorManager.mHomeWallpaperColors
+ val colorProvider = mColorManager.mProvider as ColorProvider
+ val wallpapersColorsChanged = (colorProvider.homeWallpaperColors == wallpaperColors3
+ ) || (colorProvider.lockWallpaperColors == wallpaperColors2)
+ if (wallpapersColorsChanged) {
+ colorProvider.homeWallpaperColors = wallpaperColors3
+ colorProvider.lockWallpaperColors = wallpaperColors2
+ }
+ val list = colorProvider.colorBundles
+ if (list == null || wallpapersColorsChanged) {
+ mLifecycleOwner.lifecycleScope.launch {
+ if (wallpapersColorsChanged) {
+ ColorProvider.loadSeedColors(
+ colorProvider,
+ wallpaperColors3,
+ wallpaperColors2
+ )
+ }
+ optionsFetcher.onOptionsLoaded(colorProvider.colorBundles!!)
+ }
+ } else {
+ optionsFetcher.onOptionsLoaded(list)
+ }
+ }
+ }
+
+ fun setUpColorOptionsController(optionSelectorController: OptionSelectorController) {
+ if (mSelectedColor != null && optionSelectorController.containsOption(mSelectedColor)) {
+ optionSelectorController.setSelectedOption(mSelectedColor)
+ }
+ optionSelectorController.addListener {
+ if (mSelectedColor != it) {
+ mSelectedColor = (it as ColorOption)
+ Handler(Looper.getMainLooper()).postDelayed({
+ mColorManager.setThemeBundle(this, it)
+ }, 100L)
+ return@addListener
+ }
+ }
+ }
+
+ override fun onSaveInstanceState(bundle: Bundle) {
+ val viewPager2 = mColorViewPager
+ bundle.putInt("COLOR_TAB_POSITION", viewPager2.currentItem)
+ }
+
+ init {
+ mEventLogger =
+ (InjectorProvider.getInjector() as CustomizationInjector).getUserEventLogger(activity) as ThemesUserEventLogger
+ mColorManager =
+ ColorCustomizationManager.getInstance(activity!!)!!
+ mWallpaperColorsViewModel = wallpaperColorsViewModel
+ mLifecycleOwner = lifecycleOwner
+ if (bundle != null && bundle.containsKey("COLOR_TAB_POSITION")) {
+ mTabPositionToRestore = bundle.getInt("COLOR_TAB_POSITION")
+ }
+ }
+}
diff --git a/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorSeedOption.kt b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorSeedOption.kt
new file mode 100644
index 0000000..bf25773
--- /dev/null
+++ b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorSeedOption.kt
@@ -0,0 +1,56 @@
+package com.google.android.customization.model.color
+
+import android.content.res.Configuration
+import android.graphics.PorterDuff
+import android.view.View
+import android.widget.ImageView
+import com.android.wallpaper.R
+
+class ColorSeedOption(
+ title: String?,
+ map: Map?,
+ isDefault: Boolean,
+ override val source: String,
+ index: Int,
+ private val mPreviewInfo: PreviewInfo
+) : ColorOption(
+ title!!, map!!, isDefault, index
+) {
+ private val mPreviewColorIds = intArrayOf(
+ R.id.color_preview_0,
+ R.id.color_preview_1,
+ R.id.color_preview_2,
+ R.id.color_preview_3
+ )
+
+ override fun bindThumbnailTile(view: View) {
+ val padding: Int
+ val resources = view.context.resources
+ var iterator = 0
+ val mPreviewColorTint =
+ if ((resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES) mPreviewInfo.darkColors else mPreviewInfo.lightColors
+ padding = if (view.isActivated) {
+ resources.getDimensionPixelSize(R.dimen.color_seed_option_tile_padding_selected)
+ } else {
+ resources.getDimensionPixelSize(R.dimen.color_seed_option_tile_padding)
+ }
+ while (true) {
+ if (iterator < mPreviewColorIds.size) {
+ val imageView = view.findViewById(mPreviewColorIds[iterator]) as ImageView
+ imageView.drawable.setColorFilter(mPreviewColorTint[iterator], PorterDuff.Mode.SRC)
+ imageView.setPadding(padding, padding, padding, padding)
+ iterator++
+ } else {
+ view.contentDescription =
+ view.context.getString(R.string.wallpaper_color_title)
+ return
+ }
+ }
+ }
+
+ override fun getLayoutResId(): Int {
+ return R.layout.color_seed_option
+ }
+
+ class PreviewInfo(var lightColors: IntArray, var darkColors: IntArray)
+}
diff --git a/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorUtils.java b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorUtils.java
new file mode 100644
index 0000000..843886c
--- /dev/null
+++ b/ThemePickerGoogle/src/com/google/android/customization/model/color/ColorUtils.java
@@ -0,0 +1,47 @@
+package com.google.android.customization.model.color;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public final class ColorUtils {
+ public static int sFlagId;
+ @Nullable
+ public static Resources sSysuiRes;
+
+ public static boolean isMonetEnabled(@NonNull Context context) {
+ boolean monet = SystemProperties.getBoolean("persist.systemui.flag_monet", false);
+ if (monet) {
+ return true;
+ }
+ if (sSysuiRes == null) {
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo applicationInfo = packageManager.getApplicationInfo(
+ "com.android.systemui", 0);
+ if (applicationInfo != null) {
+ sSysuiRes = packageManager.getResourcesForApplication(applicationInfo);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w("ColorUtils", "Couldn't read color flag, skipping section", e);
+ }
+ }
+ if (sFlagId == 0 && sSysuiRes != null) {
+ sFlagId = sSysuiRes.getIdentifier("flag_monet", "bool", "com.android.systemui");
+ }
+ if (sFlagId <= 0) {
+ return false;
+ }
+ return sSysuiRes.getBoolean(sFlagId);
+ }
+
+ public static String toColorString(int color) {
+ return String.format("#%06X", color & 0x00ffffff);
+ }
+}
diff --git a/ThemePickerGoogle/src/com/google/android/customization/module/GoogleCustomizationSections.java b/ThemePickerGoogle/src/com/google/android/customization/module/GoogleCustomizationSections.java
index 484ff96..0cb4406 100644
--- a/ThemePickerGoogle/src/com/google/android/customization/module/GoogleCustomizationSections.java
+++ b/ThemePickerGoogle/src/com/google/android/customization/module/GoogleCustomizationSections.java
@@ -20,6 +20,8 @@ import com.android.wallpaper.model.WallpaperSectionController;
import com.android.wallpaper.model.WorkspaceViewModel;
import com.android.wallpaper.module.CustomizationSections;
+import com.google.android.customization.model.color.ColorSectionController;
+
import java.util.ArrayList;
import java.util.List;
@@ -41,6 +43,11 @@ public final class GoogleCustomizationSections implements CustomizationSections
workspaceViewModel, sectionNavigationController, wallpaperPreviewNavigator,
savedInstanceState));
+ // Color section
+ sectionControllers.add(
+ new ColorSectionController(activity, wallpaperColorsViewModel, lifecycleOwner,
+ savedInstanceState, sectionNavigationController));
+
// Dark/Light theme section.
sectionControllers.add(new DarkModeSectionController(activity,
lifecycleOwner.getLifecycle()));
diff --git a/ThemePickerGoogle/src/com/google/android/customization/picker/color/ColorSectionView.kt b/ThemePickerGoogle/src/com/google/android/customization/picker/color/ColorSectionView.kt
new file mode 100644
index 0000000..2929b35
--- /dev/null
+++ b/ThemePickerGoogle/src/com/google/android/customization/picker/color/ColorSectionView.kt
@@ -0,0 +1,8 @@
+package com.google.android.customization.picker.color
+
+import android.content.Context
+import android.util.AttributeSet
+import com.android.wallpaper.picker.SectionView
+
+class ColorSectionView(context: Context?, attributeSet: AttributeSet?) :
+ SectionView(context, attributeSet)