Loading res/values/strings.xml +8 −0 Original line number Diff line number Diff line Loading @@ -151,6 +151,14 @@ [CHAR LIMIT=15] --> <string name="app_icons_theme_themed">themed</string> <!-- Label for an icon style option that applies no theming to app icons when selected. [CHAR LIMIT=15] --> <string name="app_icons_style_default">Default</string> <!-- Label for an icon style option that applies a monochrome theme to app icons when selected. [CHAR LIMIT=15] --> <string name="app_icons_style_minimal">Minimal</string> <!-- Title of a tab to change the app icon shape [CHAR LIMIT=15] --> <string name="app_icons_shape">Shape</string> Loading src/com/android/customization/picker/grid/domain/interactor/AppIconInteractor.kt→src/com/android/customization/picker/icon/domain/interactor/AppIconInteractor.kt +18 −2 Original line number Diff line number Diff line Loading @@ -12,13 +12,13 @@ * 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.grid.domain.interactor package com.android.customization.picker.icon.domain.interactor import com.android.customization.model.grid.ShapeOptionModel import com.android.customization.picker.grid.data.repository.ShapeRepository import com.android.customization.picker.icon.shared.model.IconStyle import com.android.customization.picker.themedicon.data.repository.ThemedIconRepository import javax.inject.Inject import javax.inject.Singleton Loading @@ -45,6 +45,22 @@ constructor( val isThemedIconEnabled: Flow<Boolean> = themedIconRepository.isActivated val iconStyles = isThemedIconAvailable.map { isThemedIconAvailable -> // TODO (b/397782741): introduce different icon styles depending on repository var styles = IconStyle.entries.toList() if (!isThemedIconAvailable) styles = styles.filter { it != IconStyle.MONOCHROME } styles } val selectedIconStyle = isThemedIconEnabled.map { when (it) { true -> IconStyle.MONOCHROME false -> IconStyle.DEFAULT } } suspend fun applyThemedIconEnabled(enabled: Boolean) = themedIconRepository.setThemedIconEnabled(enabled) Loading src/com/android/customization/picker/icon/shared/model/IconStyle.kt 0 → 100644 +24 −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.shared.model import com.android.themepicker.R enum class IconStyle(val nameResId: Int) { DEFAULT(R.string.app_icons_style_default), MONOCHROME(R.string.app_icons_style_minimal), } src/com/android/wallpaper/customization/ui/binder/AppIconFloatingSheetBinder.kt +61 −5 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ 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 @@ -32,6 +33,7 @@ import androidx.recyclerview.widget.LinearLayoutManager 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.themepicker.R import com.android.wallpaper.config.BaseFlags import com.android.wallpaper.customization.ui.util.ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption.APP_ICONS Loading Loading @@ -112,7 +114,20 @@ object AppIconFloatingSheetBinder { ) val shapeOptionList = view.requireViewById<RecyclerView>(R.id.shape_options).also { it.initShapeOptionList(view.context, shapeOptionListAdapter) it.initOptionList(view.context, shapeOptionListAdapter) } val styleOptionListAdapter = createStyleOptionItemAdapter( context = view.context, colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = isFloatingSheetActive, lifecycleOwner = lifecycleOwner, backgroundDispatcher = backgroundDispatcher, ) val styleOptionList = view.requireViewById<RecyclerView>(R.id.icon_style_options).also { it.initOptionList(view.context, styleOptionListAdapter) } val themedIconsSwitch = view.requireViewById<MaterialSwitch>(R.id.themed_icon_toggle) Loading @@ -137,6 +152,17 @@ object AppIconFloatingSheetBinder { if (isExtendibleThemeManager) { themedIconEntry.isVisible = false launch { viewModel.styleOptions.collect { options -> styleOptionListAdapter.setItems(options) { val indexToFocus = options.indexOfFirst { it.isSelected.value }.coerceAtLeast(0) (styleOptionList.layoutManager as LinearLayoutManager) .scrollToPosition(indexToFocus) } } } launch { viewModel.tabs.collect { if (it.size > 1) { Loading Loading @@ -245,6 +271,39 @@ object AppIconFloatingSheetBinder { } } private fun createStyleOptionItemAdapter( context: Context, 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 disposableHandle = imageView?.let { ShapeIconViewBinder.bindPreviewIcon( view = it, appIconDrawable = appIconDrawable as? AdaptiveIconDrawable, isThemed = iconStyle == IconStyle.MONOCHROME, colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = shouldAnimateColor, lifecycleOwner = lifecycleOwner, ) } return@OptionItemAdapter2 disposableHandle }, colorUpdateViewModel = WeakReference(colorUpdateViewModel), shouldAnimateColor = shouldAnimateColor, ) } private fun createShapeOptionItemAdapter( colorUpdateViewModel: ColorUpdateViewModel, shouldAnimateColor: () -> Boolean, Loading @@ -264,10 +323,7 @@ object AppIconFloatingSheetBinder { shouldAnimateColor = shouldAnimateColor, ) private fun RecyclerView.initShapeOptionList( context: Context, adapter: OptionItemAdapter2<ShapeIconViewModel>, ) { private fun RecyclerView.initOptionList(context: Context, adapter: OptionItemAdapter2<*>) { apply { this.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) addItemDecoration( Loading src/com/android/wallpaper/customization/ui/binder/ShapeIconViewBinder.kt +2 −2 Original line number Diff line number Diff line Loading @@ -39,7 +39,7 @@ object ShapeIconViewBinder { fun bindPreviewIcon( view: ImageView, appIconDrawable: AdaptiveIconDrawable?, shapeIcon: ShapeIconViewModel, shapeIcon: ShapeIconViewModel? = null, isThemed: Boolean, colorUpdateViewModel: ColorUpdateViewModel, shouldAnimateColor: () -> Boolean, Loading @@ -48,7 +48,7 @@ object ShapeIconViewBinder { val shapeTileDrawable = ShapeTileDrawable( context = view.context, path = shapeIcon.path, path = shapeIcon?.path, icon = appIconDrawable, isThemed = isThemed, ) Loading Loading
res/values/strings.xml +8 −0 Original line number Diff line number Diff line Loading @@ -151,6 +151,14 @@ [CHAR LIMIT=15] --> <string name="app_icons_theme_themed">themed</string> <!-- Label for an icon style option that applies no theming to app icons when selected. [CHAR LIMIT=15] --> <string name="app_icons_style_default">Default</string> <!-- Label for an icon style option that applies a monochrome theme to app icons when selected. [CHAR LIMIT=15] --> <string name="app_icons_style_minimal">Minimal</string> <!-- Title of a tab to change the app icon shape [CHAR LIMIT=15] --> <string name="app_icons_shape">Shape</string> Loading
src/com/android/customization/picker/grid/domain/interactor/AppIconInteractor.kt→src/com/android/customization/picker/icon/domain/interactor/AppIconInteractor.kt +18 −2 Original line number Diff line number Diff line Loading @@ -12,13 +12,13 @@ * 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.grid.domain.interactor package com.android.customization.picker.icon.domain.interactor import com.android.customization.model.grid.ShapeOptionModel import com.android.customization.picker.grid.data.repository.ShapeRepository import com.android.customization.picker.icon.shared.model.IconStyle import com.android.customization.picker.themedicon.data.repository.ThemedIconRepository import javax.inject.Inject import javax.inject.Singleton Loading @@ -45,6 +45,22 @@ constructor( val isThemedIconEnabled: Flow<Boolean> = themedIconRepository.isActivated val iconStyles = isThemedIconAvailable.map { isThemedIconAvailable -> // TODO (b/397782741): introduce different icon styles depending on repository var styles = IconStyle.entries.toList() if (!isThemedIconAvailable) styles = styles.filter { it != IconStyle.MONOCHROME } styles } val selectedIconStyle = isThemedIconEnabled.map { when (it) { true -> IconStyle.MONOCHROME false -> IconStyle.DEFAULT } } suspend fun applyThemedIconEnabled(enabled: Boolean) = themedIconRepository.setThemedIconEnabled(enabled) Loading
src/com/android/customization/picker/icon/shared/model/IconStyle.kt 0 → 100644 +24 −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.shared.model import com.android.themepicker.R enum class IconStyle(val nameResId: Int) { DEFAULT(R.string.app_icons_style_default), MONOCHROME(R.string.app_icons_style_minimal), }
src/com/android/wallpaper/customization/ui/binder/AppIconFloatingSheetBinder.kt +61 −5 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ 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 @@ -32,6 +33,7 @@ import androidx.recyclerview.widget.LinearLayoutManager 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.themepicker.R import com.android.wallpaper.config.BaseFlags import com.android.wallpaper.customization.ui.util.ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption.APP_ICONS Loading Loading @@ -112,7 +114,20 @@ object AppIconFloatingSheetBinder { ) val shapeOptionList = view.requireViewById<RecyclerView>(R.id.shape_options).also { it.initShapeOptionList(view.context, shapeOptionListAdapter) it.initOptionList(view.context, shapeOptionListAdapter) } val styleOptionListAdapter = createStyleOptionItemAdapter( context = view.context, colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = isFloatingSheetActive, lifecycleOwner = lifecycleOwner, backgroundDispatcher = backgroundDispatcher, ) val styleOptionList = view.requireViewById<RecyclerView>(R.id.icon_style_options).also { it.initOptionList(view.context, styleOptionListAdapter) } val themedIconsSwitch = view.requireViewById<MaterialSwitch>(R.id.themed_icon_toggle) Loading @@ -137,6 +152,17 @@ object AppIconFloatingSheetBinder { if (isExtendibleThemeManager) { themedIconEntry.isVisible = false launch { viewModel.styleOptions.collect { options -> styleOptionListAdapter.setItems(options) { val indexToFocus = options.indexOfFirst { it.isSelected.value }.coerceAtLeast(0) (styleOptionList.layoutManager as LinearLayoutManager) .scrollToPosition(indexToFocus) } } } launch { viewModel.tabs.collect { if (it.size > 1) { Loading Loading @@ -245,6 +271,39 @@ object AppIconFloatingSheetBinder { } } private fun createStyleOptionItemAdapter( context: Context, 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 disposableHandle = imageView?.let { ShapeIconViewBinder.bindPreviewIcon( view = it, appIconDrawable = appIconDrawable as? AdaptiveIconDrawable, isThemed = iconStyle == IconStyle.MONOCHROME, colorUpdateViewModel = colorUpdateViewModel, shouldAnimateColor = shouldAnimateColor, lifecycleOwner = lifecycleOwner, ) } return@OptionItemAdapter2 disposableHandle }, colorUpdateViewModel = WeakReference(colorUpdateViewModel), shouldAnimateColor = shouldAnimateColor, ) } private fun createShapeOptionItemAdapter( colorUpdateViewModel: ColorUpdateViewModel, shouldAnimateColor: () -> Boolean, Loading @@ -264,10 +323,7 @@ object AppIconFloatingSheetBinder { shouldAnimateColor = shouldAnimateColor, ) private fun RecyclerView.initShapeOptionList( context: Context, adapter: OptionItemAdapter2<ShapeIconViewModel>, ) { private fun RecyclerView.initOptionList(context: Context, adapter: OptionItemAdapter2<*>) { apply { this.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) addItemDecoration( Loading
src/com/android/wallpaper/customization/ui/binder/ShapeIconViewBinder.kt +2 −2 Original line number Diff line number Diff line Loading @@ -39,7 +39,7 @@ object ShapeIconViewBinder { fun bindPreviewIcon( view: ImageView, appIconDrawable: AdaptiveIconDrawable?, shapeIcon: ShapeIconViewModel, shapeIcon: ShapeIconViewModel? = null, isThemed: Boolean, colorUpdateViewModel: ColorUpdateViewModel, shouldAnimateColor: () -> Boolean, Loading @@ -48,7 +48,7 @@ object ShapeIconViewBinder { val shapeTileDrawable = ShapeTileDrawable( context = view.context, path = shapeIcon.path, path = shapeIcon?.path, icon = appIconDrawable, isThemed = isThemed, ) Loading