Loading res/drawable/circle_button.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2025 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. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@color/system_primary"></solid> </shape> res/layout/icon_style_option2.xml +23 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ android:clipChildren="false"> <FrameLayout android:id="@+id/option_icon" android:layout_width="@dimen/option_item_size" android:layout_height="@dimen/option_item_size" android:clipChildren="false"> Loading @@ -44,6 +45,28 @@ </FrameLayout> <FrameLayout android:id="@+id/button_icon" android:layout_width="@dimen/option_item_size" android:layout_height="@dimen/option_item_size" android:clipChildren="false" android:visibility="gone"> <ImageView android:id="@+id/button_background" android:layout_width="50dp" android:layout_height="50dp" android:layout_gravity="center" android:src="@drawable/circle_button" /> <ImageView android:id="@+id/button_foreground" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center" /> </FrameLayout> <View android:layout_width="0dp" android:layout_height="8dp" /> Loading src/com/android/customization/picker/icon/shared/model/IconStyle.kt→src/com/android/customization/picker/icon/shared/model/ThemePickerIconStyle.kt +4 −6 Original line number Diff line number Diff line Loading @@ -18,12 +18,6 @@ package com.android.customization.picker.icon.shared.model import com.android.themepicker.R interface IconStyle { val nameResId: Int fun getIsThemedIcon(): Boolean } enum class ThemePickerIconStyle(override val nameResId: Int) : IconStyle { DEFAULT(R.string.app_icons_style_default), MONOCHROME(R.string.app_icons_style_minimal); Loading @@ -31,4 +25,8 @@ enum class ThemePickerIconStyle(override val nameResId: Int) : IconStyle { override fun getIsThemedIcon(): Boolean { return this == MONOCHROME } override fun getIsExternalLink(): Boolean { return false } } src/com/android/customization/picker/icon/ui/util/ThemePickerIconStyleViewUtil.kt 0 → 100644 +47 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.icon.ui.util import android.content.Context import android.graphics.drawable.AdaptiveIconDrawable import android.graphics.drawable.Drawable import com.android.customization.picker.icon.shared.model.IconStyle import com.android.themepicker.R import com.android.wallpaper.customization.ui.binder.ShapeIconViewBinder import com.android.wallpaper.customization.ui.view.ShapeTileDrawable import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ActivityScoped import javax.inject.Inject @ActivityScoped class ThemePickerIconStyleViewUtil @Inject constructor(@ApplicationContext private val context: Context) : IconStyleViewUtil { override fun getDrawable(iconStyle: IconStyle): Drawable { val previewIconPackageName = context.resources.getString(R.string.camera_package) val appIconDrawable = ShapeIconViewBinder.loadAppIcon(context, previewIconPackageName) return ShapeTileDrawable( context = context, icon = appIconDrawable as? AdaptiveIconDrawable, isThemed = iconStyle.getIsThemedIcon(), ) } override fun getOnClick(iconStyle: IconStyle): (() -> Unit)? { return null } } src/com/android/wallpaper/customization/ui/binder/AppIconFloatingSheetBinder.kt +36 −13 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.wallpaper.customization.ui.binder import android.content.Context import android.content.res.ColorStateList import android.graphics.drawable.AdaptiveIconDrawable import android.view.View import android.view.ViewGroup import android.widget.ImageView Loading @@ -34,9 +33,11 @@ import androidx.recyclerview.widget.RecyclerView import com.android.customization.picker.common.ui.view.SingleRowListItemSpacing import com.android.customization.picker.grid.ui.viewmodel.ShapeIconViewModel import com.android.customization.picker.icon.shared.model.IconStyle import com.android.customization.picker.icon.ui.util.IconStyleViewUtil import com.android.themepicker.R import com.android.wallpaper.config.BaseFlags import com.android.wallpaper.customization.ui.util.ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption.APP_ICONS import com.android.wallpaper.customization.ui.view.ShapeTileDrawable import com.android.wallpaper.customization.ui.viewmodel.AppIconPickerViewModel import com.android.wallpaper.customization.ui.viewmodel.ThemePickerCustomizationOptionsViewModel import com.android.wallpaper.picker.customization.ui.binder.ColorUpdateBinder Loading @@ -54,6 +55,7 @@ object AppIconFloatingSheetBinder { fun bind( view: View, optionsViewModel: ThemePickerCustomizationOptionsViewModel, iconStyleViewUtil: IconStyleViewUtil, colorUpdateViewModel: ColorUpdateViewModel, lifecycleOwner: LifecycleOwner, backgroundDispatcher: CoroutineDispatcher, Loading Loading @@ -119,7 +121,7 @@ object AppIconFloatingSheetBinder { val styleOptionListAdapter = createStyleOptionItemAdapter( context = view.context, iconStyleViewUtil = iconStyleViewUtil, colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = isFloatingSheetActive, lifecycleOwner = lifecycleOwner, Loading Loading @@ -272,32 +274,53 @@ object AppIconFloatingSheetBinder { } private fun createStyleOptionItemAdapter( context: Context, iconStyleViewUtil: IconStyleViewUtil, colorUpdateViewModel: ColorUpdateViewModel, shouldAnimateColor: () -> Boolean, lifecycleOwner: LifecycleOwner, backgroundDispatcher: CoroutineDispatcher, ): OptionItemAdapter2<IconStyle> { val previewIconPackageName = context.resources.getString(R.string.camera_package) val appIconDrawable = ShapeIconViewBinder.loadAppIcon(context, previewIconPackageName) return OptionItemAdapter2( layoutResourceId = R.layout.icon_style_option2, lifecycleOwner = lifecycleOwner, backgroundDispatcher = backgroundDispatcher, bindPayload = { view: View, iconStyle: IconStyle -> val imageView = view.findViewById(R.id.foreground) as? ImageView val optionIcon = view.requireViewById<ViewGroup>(R.id.option_icon) val buttonIcon = view.requireViewById<ViewGroup>(R.id.button_icon) val drawable = iconStyleViewUtil.getDrawable(iconStyle) if (iconStyle.getIsExternalLink()) { optionIcon.visibility = View.GONE buttonIcon.visibility = View.VISIBLE val imageView = view.requireViewById<ImageView>(R.id.button_foreground) imageView.setImageDrawable(drawable) view.setOnClickListener { iconStyleViewUtil.getOnClick(iconStyle)?.invoke() } } else { optionIcon.visibility = View.VISIBLE buttonIcon.visibility = View.GONE val imageView = view.requireViewById<ImageView>(com.android.wallpaper.R.id.foreground) imageView.setImageDrawable(drawable) } // If the icon is a themed icon, bind its foreground and background color val disposableHandle = imageView?.let { // TODO (b/397782741): bind icons correctly for additional themes ShapeIconViewBinder.bindPreviewIcon( view = it, appIconDrawable = appIconDrawable as? AdaptiveIconDrawable, isThemed = iconStyle.getIsThemedIcon(), if (iconStyle.getIsThemedIcon()) { (drawable as? ShapeTileDrawable)?.let { ShapeIconViewBinder.bindPreviewIconColor( shapeTileDrawable = it, colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = shouldAnimateColor, lifecycleOwner = lifecycleOwner, ) } } else if (iconStyle.getIsExternalLink()) { ShapeIconViewBinder.bindButtonIconColor( foreground = buttonIcon.requireViewById(R.id.button_foreground), background = buttonIcon.requireViewById(R.id.button_background), colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = shouldAnimateColor, lifecycleOwner = lifecycleOwner, ) } else null return@OptionItemAdapter2 disposableHandle }, colorUpdateViewModel = WeakReference(colorUpdateViewModel), Loading Loading
res/drawable/circle_button.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2025 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. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@color/system_primary"></solid> </shape>
res/layout/icon_style_option2.xml +23 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ android:clipChildren="false"> <FrameLayout android:id="@+id/option_icon" android:layout_width="@dimen/option_item_size" android:layout_height="@dimen/option_item_size" android:clipChildren="false"> Loading @@ -44,6 +45,28 @@ </FrameLayout> <FrameLayout android:id="@+id/button_icon" android:layout_width="@dimen/option_item_size" android:layout_height="@dimen/option_item_size" android:clipChildren="false" android:visibility="gone"> <ImageView android:id="@+id/button_background" android:layout_width="50dp" android:layout_height="50dp" android:layout_gravity="center" android:src="@drawable/circle_button" /> <ImageView android:id="@+id/button_foreground" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center" /> </FrameLayout> <View android:layout_width="0dp" android:layout_height="8dp" /> Loading
src/com/android/customization/picker/icon/shared/model/IconStyle.kt→src/com/android/customization/picker/icon/shared/model/ThemePickerIconStyle.kt +4 −6 Original line number Diff line number Diff line Loading @@ -18,12 +18,6 @@ package com.android.customization.picker.icon.shared.model import com.android.themepicker.R interface IconStyle { val nameResId: Int fun getIsThemedIcon(): Boolean } enum class ThemePickerIconStyle(override val nameResId: Int) : IconStyle { DEFAULT(R.string.app_icons_style_default), MONOCHROME(R.string.app_icons_style_minimal); Loading @@ -31,4 +25,8 @@ enum class ThemePickerIconStyle(override val nameResId: Int) : IconStyle { override fun getIsThemedIcon(): Boolean { return this == MONOCHROME } override fun getIsExternalLink(): Boolean { return false } }
src/com/android/customization/picker/icon/ui/util/ThemePickerIconStyleViewUtil.kt 0 → 100644 +47 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.icon.ui.util import android.content.Context import android.graphics.drawable.AdaptiveIconDrawable import android.graphics.drawable.Drawable import com.android.customization.picker.icon.shared.model.IconStyle import com.android.themepicker.R import com.android.wallpaper.customization.ui.binder.ShapeIconViewBinder import com.android.wallpaper.customization.ui.view.ShapeTileDrawable import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ActivityScoped import javax.inject.Inject @ActivityScoped class ThemePickerIconStyleViewUtil @Inject constructor(@ApplicationContext private val context: Context) : IconStyleViewUtil { override fun getDrawable(iconStyle: IconStyle): Drawable { val previewIconPackageName = context.resources.getString(R.string.camera_package) val appIconDrawable = ShapeIconViewBinder.loadAppIcon(context, previewIconPackageName) return ShapeTileDrawable( context = context, icon = appIconDrawable as? AdaptiveIconDrawable, isThemed = iconStyle.getIsThemedIcon(), ) } override fun getOnClick(iconStyle: IconStyle): (() -> Unit)? { return null } }
src/com/android/wallpaper/customization/ui/binder/AppIconFloatingSheetBinder.kt +36 −13 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.wallpaper.customization.ui.binder import android.content.Context import android.content.res.ColorStateList import android.graphics.drawable.AdaptiveIconDrawable import android.view.View import android.view.ViewGroup import android.widget.ImageView Loading @@ -34,9 +33,11 @@ import androidx.recyclerview.widget.RecyclerView import com.android.customization.picker.common.ui.view.SingleRowListItemSpacing import com.android.customization.picker.grid.ui.viewmodel.ShapeIconViewModel import com.android.customization.picker.icon.shared.model.IconStyle import com.android.customization.picker.icon.ui.util.IconStyleViewUtil import com.android.themepicker.R import com.android.wallpaper.config.BaseFlags import com.android.wallpaper.customization.ui.util.ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption.APP_ICONS import com.android.wallpaper.customization.ui.view.ShapeTileDrawable import com.android.wallpaper.customization.ui.viewmodel.AppIconPickerViewModel import com.android.wallpaper.customization.ui.viewmodel.ThemePickerCustomizationOptionsViewModel import com.android.wallpaper.picker.customization.ui.binder.ColorUpdateBinder Loading @@ -54,6 +55,7 @@ object AppIconFloatingSheetBinder { fun bind( view: View, optionsViewModel: ThemePickerCustomizationOptionsViewModel, iconStyleViewUtil: IconStyleViewUtil, colorUpdateViewModel: ColorUpdateViewModel, lifecycleOwner: LifecycleOwner, backgroundDispatcher: CoroutineDispatcher, Loading Loading @@ -119,7 +121,7 @@ object AppIconFloatingSheetBinder { val styleOptionListAdapter = createStyleOptionItemAdapter( context = view.context, iconStyleViewUtil = iconStyleViewUtil, colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = isFloatingSheetActive, lifecycleOwner = lifecycleOwner, Loading Loading @@ -272,32 +274,53 @@ object AppIconFloatingSheetBinder { } private fun createStyleOptionItemAdapter( context: Context, iconStyleViewUtil: IconStyleViewUtil, colorUpdateViewModel: ColorUpdateViewModel, shouldAnimateColor: () -> Boolean, lifecycleOwner: LifecycleOwner, backgroundDispatcher: CoroutineDispatcher, ): OptionItemAdapter2<IconStyle> { val previewIconPackageName = context.resources.getString(R.string.camera_package) val appIconDrawable = ShapeIconViewBinder.loadAppIcon(context, previewIconPackageName) return OptionItemAdapter2( layoutResourceId = R.layout.icon_style_option2, lifecycleOwner = lifecycleOwner, backgroundDispatcher = backgroundDispatcher, bindPayload = { view: View, iconStyle: IconStyle -> val imageView = view.findViewById(R.id.foreground) as? ImageView val optionIcon = view.requireViewById<ViewGroup>(R.id.option_icon) val buttonIcon = view.requireViewById<ViewGroup>(R.id.button_icon) val drawable = iconStyleViewUtil.getDrawable(iconStyle) if (iconStyle.getIsExternalLink()) { optionIcon.visibility = View.GONE buttonIcon.visibility = View.VISIBLE val imageView = view.requireViewById<ImageView>(R.id.button_foreground) imageView.setImageDrawable(drawable) view.setOnClickListener { iconStyleViewUtil.getOnClick(iconStyle)?.invoke() } } else { optionIcon.visibility = View.VISIBLE buttonIcon.visibility = View.GONE val imageView = view.requireViewById<ImageView>(com.android.wallpaper.R.id.foreground) imageView.setImageDrawable(drawable) } // If the icon is a themed icon, bind its foreground and background color val disposableHandle = imageView?.let { // TODO (b/397782741): bind icons correctly for additional themes ShapeIconViewBinder.bindPreviewIcon( view = it, appIconDrawable = appIconDrawable as? AdaptiveIconDrawable, isThemed = iconStyle.getIsThemedIcon(), if (iconStyle.getIsThemedIcon()) { (drawable as? ShapeTileDrawable)?.let { ShapeIconViewBinder.bindPreviewIconColor( shapeTileDrawable = it, colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = shouldAnimateColor, lifecycleOwner = lifecycleOwner, ) } } else if (iconStyle.getIsExternalLink()) { ShapeIconViewBinder.bindButtonIconColor( foreground = buttonIcon.requireViewById(R.id.button_foreground), background = buttonIcon.requireViewById(R.id.button_background), colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = shouldAnimateColor, lifecycleOwner = lifecycleOwner, ) } else null return@OptionItemAdapter2 disposableHandle }, colorUpdateViewModel = WeakReference(colorUpdateViewModel), Loading