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)