summaryrefslogtreecommitdiff
path: root/src/com/android
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-13 00:18:39 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-13 00:18:39 +0000
commit3448c3edb1876809aa9ff3e8d441c110aa3c07fb (patch)
treeaba9bb80910323ff849285ab3f1f610e64dc977f /src/com/android
parent3cc68260bf5367ff502c46322e5673e4b3ad84be (diff)
parentf93898e32e7ae418e8ec7ad25000e2c0a9c8c613 (diff)
downloadThemePicker-3448c3edb1876809aa9ff3e8d441c110aa3c07fb.tar.gz
Snap for 11211409 from f93898e32e7ae418e8ec7ad25000e2c0a9c8c613 to sdk-release
Change-Id: I67b5a8652c40bebba4a83549e0d77d9cd680457c
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/customization/model/CustomizationManager.java3
-rw-r--r--src/com/android/customization/model/color/ColorProvider.kt11
-rw-r--r--src/com/android/customization/model/color/ColorSectionController.java538
-rw-r--r--src/com/android/customization/model/color/ColorUtils.kt68
-rw-r--r--src/com/android/customization/model/color/WallpaperColorResources.java52
-rw-r--r--src/com/android/customization/model/grid/GridOption.java18
-rw-r--r--src/com/android/customization/model/grid/GridOptionsManager.java5
-rw-r--r--src/com/android/customization/model/grid/LauncherGridOptionsProvider.java16
-rw-r--r--src/com/android/customization/model/grid/data/repository/GridRepository.kt78
-rw-r--r--src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt12
-rw-r--r--src/com/android/customization/model/grid/ui/binder/GridScreenBinder.kt14
-rw-r--r--src/com/android/customization/model/grid/ui/fragment/GridFragment2.kt82
-rw-r--r--src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java6
-rw-r--r--src/com/android/customization/module/CustomizationInjector.kt3
-rw-r--r--src/com/android/customization/module/DefaultCustomizationSections.java72
-rw-r--r--src/com/android/customization/module/StatsLogUserEventLogger.java90
-rw-r--r--src/com/android/customization/module/SysUiStatsLogger.kt82
-rw-r--r--src/com/android/customization/module/ThemePickerInjector.kt101
-rw-r--r--src/com/android/customization/picker/CustomizationPickerApplication.java31
-rw-r--r--src/com/android/customization/picker/HorizontalTouchMovementAwareNestedScrollView.kt64
-rw-r--r--src/com/android/customization/picker/WallpaperPreviewer.java2
-rw-r--r--src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt20
-rw-r--r--src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt3
-rw-r--r--src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt1
-rw-r--r--src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt30
-rw-r--r--src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt3
-rw-r--r--src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt93
-rw-r--r--src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt10
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt92
-rw-r--r--src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt7
-rw-r--r--src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt51
-rw-r--r--src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt8
-rw-r--r--src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt8
-rw-r--r--src/com/android/customization/picker/color/ColorSectionView.java33
-rw-r--r--src/com/android/customization/picker/color/data/repository/ColorPickerRepository.kt3
-rw-r--r--src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt15
-rw-r--r--src/com/android/customization/picker/color/data/repository/FakeColorPickerRepository.kt3
-rw-r--r--src/com/android/customization/picker/color/domain/interactor/ColorPickerInteractor.kt2
-rw-r--r--src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt36
-rw-r--r--src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt28
-rw-r--r--src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt112
-rw-r--r--src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt22
-rw-r--r--src/com/android/customization/picker/preview/ui/section/PreviewWithThemeSectionController.kt8
-rw-r--r--src/com/android/customization/picker/preview/ui/viewmodel/PreviewWithThemeViewModel.kt15
-rw-r--r--src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt6
-rw-r--r--src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt10
-rw-r--r--src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordanceSectionViewBinder.kt2
-rw-r--r--src/com/android/customization/picker/quickaffordance/ui/fragment/KeyguardQuickAffordancePickerFragment.kt16
-rw-r--r--src/com/android/customization/widget/GridTileDrawable.java4
49 files changed, 690 insertions, 1299 deletions
diff --git a/src/com/android/customization/model/CustomizationManager.java b/src/com/android/customization/model/CustomizationManager.java
index 104cc837..e6d3872c 100644
--- a/src/com/android/customization/model/CustomizationManager.java
+++ b/src/com/android/customization/model/CustomizationManager.java
@@ -72,6 +72,9 @@ public interface CustomizationManager<T extends CustomizationOption> {
*/
void apply(T option, Callback callback);
+ /** Preview the given option without committing the change. */
+ default void preview(T option) {}
+
/**
* Loads the available options for the type of Customization managed by this class, calling the
* given callback when done.
diff --git a/src/com/android/customization/model/color/ColorProvider.kt b/src/com/android/customization/model/color/ColorProvider.kt
index 63d298d1..74a4051b 100644
--- a/src/com/android/customization/model/color/ColorProvider.kt
+++ b/src/com/android/customization/model/color/ColorProvider.kt
@@ -16,6 +16,7 @@
package com.android.customization.model.color
import android.app.WallpaperColors
+import android.app.WallpaperManager
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Resources
@@ -38,7 +39,6 @@ import com.android.customization.picker.color.shared.model.ColorType
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
import com.android.wallpaper.R
-import com.android.wallpaper.compat.WallpaperManagerCompat
import com.android.wallpaper.module.InjectorProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -130,9 +130,9 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
private fun isLockScreenWallpaperLastApplied(): Boolean {
// The WallpaperId increases every time a new wallpaper is set, so the larger wallpaper id
// is the most recently set wallpaper
- val manager = InjectorProvider.getInjector().getWallpaperManagerCompat(mContext)
- return manager.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK) >
- manager.getWallpaperId(WallpaperManagerCompat.FLAG_SYSTEM)
+ val manager = WallpaperManager.getInstance(mContext)
+ return manager.getWallpaperId(WallpaperManager.FLAG_LOCK) >
+ manager.getWallpaperId(WallpaperManager.FLAG_SYSTEM)
}
private fun loadSeedColors(
@@ -432,7 +432,8 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
val extractor = ColorBundlePreviewExtractor(mContext)
val bundles: MutableList<ColorOption> = ArrayList()
- val bundleNames = getItemsFromStub(COLOR_BUNDLES_ARRAY_NAME)
+ val bundleNames =
+ if (isAvailable) getItemsFromStub(COLOR_BUNDLES_ARRAY_NAME) else emptyArray()
// Color option index value starts from 1.
var index = 1
val maxPresetColors = if (themeStyleEnabled) bundleNames.size else MAX_PRESET_COLORS
diff --git a/src/com/android/customization/model/color/ColorSectionController.java b/src/com/android/customization/model/color/ColorSectionController.java
deleted file mode 100644
index be051ac0..00000000
--- a/src/com/android/customization/model/color/ColorSectionController.java
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * Copyright (C) 2022 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.model.color;
-
-import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
-import static android.view.View.VISIBLE;
-
-import static com.android.customization.model.color.ColorOptionsProvider.COLOR_SOURCE_HOME;
-import static com.android.customization.model.color.ColorOptionsProvider.COLOR_SOURCE_LOCK;
-import static com.android.customization.model.color.ColorOptionsProvider.COLOR_SOURCE_PRESET;
-import static com.android.customization.widget.OptionSelectorController.CheckmarkStyle.CENTER;
-
-import android.app.Activity;
-import android.app.WallpaperColors;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.stats.style.StyleEnums;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-
-import androidx.annotation.Nullable;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.viewpager2.widget.ViewPager2;
-
-import com.android.customization.model.CustomizationManager;
-import com.android.customization.model.theme.OverlayManagerCompat;
-import com.android.customization.module.CustomizationInjector;
-import com.android.customization.module.ThemesUserEventLogger;
-import com.android.customization.picker.color.ColorSectionView;
-import com.android.customization.widget.OptionSelectorController;
-import com.android.wallpaper.R;
-import com.android.wallpaper.model.CustomizationSectionController;
-import com.android.wallpaper.model.WallpaperColorsViewModel;
-import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.widget.PageIndicator;
-import com.android.wallpaper.widget.SeparatedTabLayout;
-
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Optional;
-
-/**
- * Color section view's controller for the logic of color customization.
- */
-public class ColorSectionController implements CustomizationSectionController<ColorSectionView> {
-
- private static final String TAG = "ColorSectionController";
- private static final String KEY_COLOR_TAB_POSITION = "COLOR_TAB_POSITION";
- private static final String KEY_COLOR_PAGE_POSITION = "COLOR_PAGE_POSITION";
- private static final String ID_VIEWPAGER = "ColorSectionController_colorSectionViewPager";
- private static final String ID_ITEMVIEW = "ColorSectionController_itemView";
- private static final String ID_CONTAINER = "ColorSectionController_container";
- private static final long MIN_COLOR_APPLY_PERIOD = 500L;
-
- private static final int WALLPAPER_TAB_INDEX = 0;
- private static final int PRESET_TAB_INDEX = 1;
-
- private final ThemesUserEventLogger mEventLogger;
- private final ColorCustomizationManager mColorManager;
- private final WallpaperColorsViewModel mWallpaperColorsViewModel;
- private final LifecycleOwner mLifecycleOwner;
- private final ColorSectionAdapter mColorSectionAdapter = new ColorSectionAdapter();
-
- private List<ColorOption> mWallpaperColorOptions = new ArrayList<>();
- private List<ColorOption> mPresetColorOptions = new ArrayList<>();
- private ViewPager2 mColorSectionViewPager;
- private ColorOption mSelectedColor;
- private SeparatedTabLayout mTabLayout;
- @Nullable private WallpaperColors mHomeWallpaperColors;
- @Nullable private WallpaperColors mLockWallpaperColors;
- // Uses a boolean value to indicate whether wallpaper color is ready because WallpaperColors
- // maybe be null when it's ready.
- private boolean mHomeWallpaperColorsReady;
- private boolean mLockWallpaperColorsReady;
- private Optional<Integer> mTabPositionToRestore = Optional.empty();
- private Optional<Integer>[] mPagePositionToRestore =
- new Optional[]{Optional.empty(), Optional.empty()};
- private long mLastColorApplyingTime = 0L;
- private ColorSectionView mColorSectionView;
-
- private static int getNumPages(int optionsPerPage, int totalOptions) {
- return (int) Math.ceil((float) totalOptions / optionsPerPage);
- }
-
- public ColorSectionController(Activity activity, WallpaperColorsViewModel viewModel,
- LifecycleOwner lifecycleOwner, @Nullable Bundle savedInstanceState) {
- CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
- mEventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(activity);
- mColorManager = ColorCustomizationManager.getInstance(activity,
- new OverlayManagerCompat(activity));
- mWallpaperColorsViewModel = viewModel;
- mLifecycleOwner = lifecycleOwner;
-
- if (savedInstanceState != null) {
- if (savedInstanceState.containsKey(KEY_COLOR_TAB_POSITION)) {
- mTabPositionToRestore = Optional.of(
- savedInstanceState.getInt(KEY_COLOR_TAB_POSITION));
- }
-
- for (int i = 0; i < mPagePositionToRestore.length; i++) {
- String keyColorPage = getPagePositionKey(i);
- if (keyColorPage != null && savedInstanceState.containsKey(keyColorPage)) {
- setPagePositionToRestore(i, savedInstanceState.getInt(keyColorPage));
- }
- }
- }
- }
-
- private String getPagePositionKey(int index) {
- return String.format(Locale.US, "%s_%d", KEY_COLOR_PAGE_POSITION, index);
- }
-
- private void setPagePositionToRestore(int pagePositionKeyIndex, int pagePosition) {
- if (pagePositionKeyIndex >= 0 && pagePositionKeyIndex < mPagePositionToRestore.length) {
- mPagePositionToRestore[pagePositionKeyIndex] = Optional.of(pagePosition);
- }
- }
-
- private int getPagePositionToRestore(int pagePositionKeyIndex, int defaultPagePosition) {
- if (pagePositionKeyIndex >= 0 && pagePositionKeyIndex < mPagePositionToRestore.length) {
- return mPagePositionToRestore[pagePositionKeyIndex].orElse(defaultPagePosition);
- }
- return 0;
- }
-
- @Override
- public boolean isAvailable(@Nullable Context context) {
- return context != null && ColorUtils.isMonetEnabled(context) && mColorManager.isAvailable();
- }
-
- @Override
- public ColorSectionView createView(Context context) {
- mColorSectionView = (ColorSectionView) LayoutInflater.from(context).inflate(
- R.layout.color_section_view, /* root= */ null);
- mColorSectionViewPager = mColorSectionView.findViewById(R.id.color_section_view_pager);
- mColorSectionViewPager.setAccessibilityDelegate(createAccessibilityDelegate(ID_VIEWPAGER));
- mColorSectionViewPager.setAdapter(mColorSectionAdapter);
- mColorSectionViewPager.setUserInputEnabled(false);
- if (ColorProvider.themeStyleEnabled) {
- mColorSectionViewPager.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- }
- mTabLayout = mColorSectionView.findViewById(R.id.separated_tabs);
- mColorSectionAdapter.setNumColors(context.getResources().getInteger(
- R.integer.options_grid_num_columns));
- // TODO(b/202145216): Use just 2 views when tapping either button on top.
- mTabLayout.setViewPager(mColorSectionViewPager);
-
- mWallpaperColorsViewModel.getHomeWallpaperColorsLiveData().observe(mLifecycleOwner,
- homeColors -> {
- mHomeWallpaperColors = homeColors;
- mHomeWallpaperColorsReady = true;
- maybeLoadColors();
- });
- mWallpaperColorsViewModel.getLockWallpaperColorsLiveData().observe(mLifecycleOwner,
- lockColors -> {
- mLockWallpaperColors = lockColors;
- mLockWallpaperColorsReady = true;
- maybeLoadColors();
- });
- return mColorSectionView;
- }
-
- @Override
- public void onSaveInstanceState(Bundle savedInstanceState) {
- if (mColorSectionViewPager != null) {
- savedInstanceState.putInt(KEY_COLOR_TAB_POSITION,
- mColorSectionViewPager.getCurrentItem());
-
- for (int i = 0; i < mPagePositionToRestore.length; i++) {
- savedInstanceState.putInt(getPagePositionKey(i), getPagePositionToRestore(i, 0));
- }
- }
- }
-
- private void maybeLoadColors() {
- if (mHomeWallpaperColorsReady && mLockWallpaperColorsReady) {
- mColorManager.setWallpaperColors(mHomeWallpaperColors, mLockWallpaperColors);
- loadColorOptions(/* reload= */ false);
- }
- }
-
- private void loadColorOptions(boolean reload) {
- mColorManager.fetchOptions(new CustomizationManager.OptionsFetchedListener<ColorOption>() {
- @Override
- public void onOptionsLoaded(List<ColorOption> options) {
- List<ColorOption> wallpaperColorOptions = new ArrayList<>();
- List<ColorOption> presetColorOptions = new ArrayList<>();
- for (ColorOption option : options) {
- if (option instanceof ColorSeedOption) {
- wallpaperColorOptions.add(option);
- } else if (option instanceof ColorBundle) {
- presetColorOptions.add(option);
- }
- }
- mWallpaperColorOptions = wallpaperColorOptions;
- mPresetColorOptions = presetColorOptions;
- mSelectedColor = findActiveColorOption(mWallpaperColorOptions,
- mPresetColorOptions);
- mTabLayout.post(()-> setUpColorViewPager());
- }
-
- @Override
- public void onError(@Nullable Throwable throwable) {
- if (throwable != null) {
- Log.e(TAG, "Error loading theme bundles", throwable);
- }
- }
- }, reload);
- }
-
- private void setUpColorViewPager() {
- mColorSectionAdapter.notifyDataSetChanged();
-
- if (mTabLayout != null && mTabLayout.getTabCount() == 0) {
- mTabLayout.addTab(mTabLayout.newTab().setText(R.string.wallpaper_color_tab),
- WALLPAPER_TAB_INDEX);
- mTabLayout.addTab(mTabLayout.newTab().setText(R.string.preset_color_tab),
- PRESET_TAB_INDEX);
- }
-
- if (mWallpaperColorOptions.isEmpty()) {
- // Select preset tab and disable wallpaper tab.
- mTabLayout.getTabAt(WALLPAPER_TAB_INDEX).view.setEnabled(false);
- mColorSectionViewPager.setCurrentItem(PRESET_TAB_INDEX, /* smoothScroll= */ false);
- return;
- }
-
- mColorSectionViewPager.setCurrentItem(
- mTabPositionToRestore.orElseGet(
- () -> COLOR_SOURCE_PRESET.equals(mColorManager.getCurrentColorSource())
- ? PRESET_TAB_INDEX
- : WALLPAPER_TAB_INDEX),
- /* smoothScroll= */ false);
-
- // Disable "wallpaper colors" and "basic colors" swiping for new color style.
- mColorSectionViewPager.setUserInputEnabled(!ColorProvider.themeStyleEnabled);
- }
-
- private void setupColorPages(ViewPager2 container, int colorsPerPage, int sectionPosition,
- List<ColorOption> options, PageIndicator pageIndicator) {
- container.setAdapter(new ColorPageAdapter(options, /* pageEnabled= */ true,
- colorsPerPage));
- if (ColorProvider.themeStyleEnabled) {
- // Update page index to show selected items.
- int selectedIndex = options.indexOf(mSelectedColor);
- if (colorsPerPage != 0) {
- int pageIndex = selectedIndex / colorsPerPage;
- int position = getPagePositionToRestore(sectionPosition, pageIndex);
- container.setCurrentItem(position, /* smoothScroll= */ false);
- }
- pageIndicator.setNumPages(getNumPages(colorsPerPage, options.size()));
- registerOnPageChangeCallback(sectionPosition, container, pageIndicator);
- }
- }
-
- private void registerOnPageChangeCallback(int sectionPosition, ViewPager2 container,
- PageIndicator pageIndicator) {
- container.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
- @Override
- public void onPageSelected(int position) {
- super.onPageSelected(position);
- if (mColorSectionViewPager.getCurrentItem() == sectionPosition) {
- pageIndicator.setLocation(getPagePosition(pageIndicator, position));
- setPagePositionToRestore(sectionPosition, position);
- }
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset,
- int positionOffsetPixels) {
- super.onPageScrolled(position, positionOffset, positionOffsetPixels);
- if (mColorSectionViewPager.getCurrentItem() == sectionPosition) {
- pageIndicator.setLocation(getPagePosition(pageIndicator, position));
- setPagePositionToRestore(sectionPosition, position);
- }
- }
-
- private int getPagePosition(PageIndicator pageIndicator, int position) {
- return pageIndicator.isLayoutRtl() ? pageIndicator.getChildCount() - 1 - position
- : position;
- }
- });
- }
-
- private void setupColorOptions(RecyclerView container, List<ColorOption> colorOptions,
- boolean pageEnabled, int index, int colorsPerPage) {
- int totalSize = colorOptions.size();
- if (totalSize == 0) {
- return;
- }
-
- List<ColorOption> subOptions;
- if (pageEnabled && ColorProvider.themeStyleEnabled) {
- subOptions = colorOptions.subList(colorsPerPage * index,
- Math.min(colorsPerPage * (index + 1), totalSize));
- } else {
- subOptions = colorOptions;
- }
-
- OptionSelectorController<ColorOption> adaptiveController = new OptionSelectorController<>(
- container, subOptions, /* useGrid= */ true, CENTER);
- adaptiveController.initOptions(mColorManager);
- setUpColorOptionsController(adaptiveController);
- }
-
- private ColorOption findActiveColorOption(List<ColorOption> wallpaperColorOptions,
- List<ColorOption> presetColorOptions) {
- ColorOption activeColorOption = null;
- for (ColorOption colorOption : Lists.newArrayList(
- Iterables.concat(wallpaperColorOptions, presetColorOptions))) {
- if (colorOption.isActive(mColorManager)) {
- activeColorOption = colorOption;
- break;
- }
- }
- // Use the first one option by default. This should not happen as above should have an
- // active option found.
- if (activeColorOption == null) {
- activeColorOption = wallpaperColorOptions.isEmpty()
- ? presetColorOptions.get(0)
- : wallpaperColorOptions.get(0);
- }
- return activeColorOption;
- }
-
- private void setUpColorOptionsController(
- OptionSelectorController<ColorOption> optionSelectorController) {
- if (mSelectedColor != null && optionSelectorController.containsOption(mSelectedColor)) {
- optionSelectorController.setSelectedOption(mSelectedColor);
- }
-
- optionSelectorController.addListener(selectedOption -> {
- ColorOption selectedColor = (ColorOption) selectedOption;
- if (mSelectedColor.equals(selectedColor)) {
- return;
- }
- mSelectedColor = (ColorOption) selectedOption;
- // Post with delay for color option to run ripple.
- new Handler().postDelayed(()-> applyColor(mSelectedColor), /* delayMillis= */ 100);
- });
- }
-
- private void applyColor(ColorOption colorOption) {
- if (SystemClock.elapsedRealtime() - mLastColorApplyingTime < MIN_COLOR_APPLY_PERIOD) {
- return;
- }
- mLastColorApplyingTime = SystemClock.elapsedRealtime();
- mColorManager.apply(colorOption, new CustomizationManager.Callback() {
- @Override
- public void onSuccess() {
- mColorSectionView.announceForAccessibility(
- mColorSectionView.getContext().getString(R.string.color_changed));
- mEventLogger.logColorApplied(getColorAction(colorOption), colorOption);
- }
-
- @Override
- public void onError(@Nullable Throwable throwable) {
- Log.w(TAG, "Apply theme with error: " + throwable);
- }
- });
- }
-
- private int getColorAction(ColorOption colorOption) {
- int action = StyleEnums.DEFAULT_ACTION;
- boolean isForBoth = mLockWallpaperColors == null || mLockWallpaperColors.equals(
- mHomeWallpaperColors);
-
- if (TextUtils.equals(colorOption.getSource(), COLOR_SOURCE_PRESET)) {
- action = StyleEnums.COLOR_PRESET_APPLIED;
- } else if (isForBoth) {
- action = StyleEnums.COLOR_WALLPAPER_HOME_LOCK_APPLIED;
- } else {
- switch (colorOption.getSource()) {
- case COLOR_SOURCE_HOME:
- action = StyleEnums.COLOR_WALLPAPER_HOME_APPLIED;
- break;
- case COLOR_SOURCE_LOCK:
- action = StyleEnums.COLOR_WALLPAPER_LOCK_APPLIED;
- break;
- }
- }
- return action;
- }
-
- private View.AccessibilityDelegate createAccessibilityDelegate(String id) {
- return new View.AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- info.setUniqueId(id);
- }
- };
- }
-
- private class ColorSectionAdapter extends
- RecyclerView.Adapter<ColorSectionAdapter.ColorPageViewHolder> {
-
- private final int mItemCounts = new int[]{WALLPAPER_TAB_INDEX, PRESET_TAB_INDEX}.length;
- private int mNumColors;
-
- @Override
- public int getItemCount() {
- return mItemCounts;
- }
-
- @Override
- public void onBindViewHolder(ColorPageViewHolder viewHolder, int position) {
- switch (position) {
- case WALLPAPER_TAB_INDEX:
- setupColorPages(viewHolder.mContainer, mNumColors, position,
- mWallpaperColorOptions, viewHolder.mPageIndicator);
- break;
- case PRESET_TAB_INDEX:
- setupColorPages(viewHolder.mContainer, mNumColors, position,
- mPresetColorOptions, viewHolder.mPageIndicator);
- break;
- default:
- break;
- }
- }
-
- @Override
- public ColorPageViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
- return new ColorPageViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(
- viewType, viewGroup, false));
- }
-
- @Override
- public int getItemViewType(int position) {
- return R.layout.color_pages_view;
- }
-
- public void setNumColors(int numColors) {
- mNumColors = numColors;
- }
-
- private class ColorPageViewHolder extends RecyclerView.ViewHolder {
- private ViewPager2 mContainer;
- private PageIndicator mPageIndicator;
-
- ColorPageViewHolder(View itemView) {
- super(itemView);
- mContainer = itemView.findViewById(R.id.color_page_container);
- // Correct scrolling goes under collapsing toolbar while scrolling oclor options.
- mContainer.getChildAt(0).setNestedScrollingEnabled(false);
- mPageIndicator = itemView.findViewById(R.id.color_page_indicator);
- if (ColorProvider.themeStyleEnabled) {
- mPageIndicator.setVisibility(VISIBLE);
- }
- itemView.setAccessibilityDelegate(createAccessibilityDelegate(ID_ITEMVIEW));
- mContainer.setAccessibilityDelegate(createAccessibilityDelegate(ID_CONTAINER));
- }
- }
- }
-
- private class ColorPageAdapter extends
- RecyclerView.Adapter<ColorPageAdapter.ColorOptionViewHolder> {
-
- private final boolean mPageEnabled;
- private final List<ColorOption> mColorOptions;
- private final int mColorsPerPage;
-
- private ColorPageAdapter(List<ColorOption> colorOptions, boolean pageEnabled,
- int colorsPerPage) {
- mPageEnabled = pageEnabled;
- mColorOptions = colorOptions;
- mColorsPerPage = colorsPerPage;
- }
-
- @Override
- public int getItemCount() {
- if (!mPageEnabled || !ColorProvider.themeStyleEnabled) {
- return 1;
- }
- // Color page size.
- return getNumPages(mColorsPerPage, mColorOptions.size());
- }
-
- @Override
- public void onBindViewHolder(ColorOptionViewHolder viewHolder, int position) {
- setupColorOptions(viewHolder.mContainer, mColorOptions, mPageEnabled, position,
- mColorsPerPage);
- }
-
- @Override
- public ColorOptionViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
- return new ColorOptionViewHolder(
- LayoutInflater.from(viewGroup.getContext()).inflate(viewType, viewGroup,
- false));
- }
-
- @Override
- public int getItemViewType(int position) {
- return R.layout.color_options_view;
- }
-
- private class ColorOptionViewHolder extends RecyclerView.ViewHolder {
- private RecyclerView mContainer;
-
- ColorOptionViewHolder(View itemView) {
- super(itemView);
- mContainer = itemView.findViewById(R.id.color_option_container);
- // Sets layout with margins to separate color options.
- final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
- mContainer.getLayoutParams());
- final int margin = itemView.getContext().getResources().getDimensionPixelSize(
- R.dimen.section_horizontal_padding);
- layoutParams.setMargins(margin, /* top= */ 0, margin, /* bottom= */ 0);
- mContainer.setLayoutParams(layoutParams);
- }
- }
- }
-}
diff --git a/src/com/android/customization/model/color/ColorUtils.kt b/src/com/android/customization/model/color/ColorUtils.kt
deleted file mode 100644
index 9d925e13..00000000
--- a/src/com/android/customization/model/color/ColorUtils.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2022 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.model.color
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.content.res.Resources
-import android.os.SystemProperties
-import android.util.Log
-import androidx.annotation.ColorInt
-
-/** Utility to wrap Monet's color extraction */
-object ColorUtils {
- private const val TAG = "ColorUtils"
- private const val MONET_FLAG = "flag_monet"
- private var sSysuiRes: Resources? = null
- private var sFlagId = 0
-
- /** Returns true if color extraction is enabled in systemui. */
- @JvmStatic
- fun isMonetEnabled(context: Context): Boolean {
- var monetEnabled = SystemProperties.getBoolean("persist.systemui.flag_monet", false)
- if (!monetEnabled) {
- if (sSysuiRes == null) {
- try {
- val pm = context.packageManager
- val sysUIInfo =
- pm.getApplicationInfo(
- "com.android.systemui",
- PackageManager.GET_META_DATA or PackageManager.MATCH_SYSTEM_ONLY
- )
- if (sysUIInfo != null) {
- sSysuiRes = pm.getResourcesForApplication(sysUIInfo)
- }
- } catch (e: PackageManager.NameNotFoundException) {
- Log.w(TAG, "Couldn't read color flag, skipping section", e)
- }
- }
- if (sFlagId == 0) {
- sFlagId =
- if (sSysuiRes == null) 0
- else sSysuiRes!!.getIdentifier(MONET_FLAG, "bool", "com.android.systemui")
- }
- if (sFlagId > 0) {
- monetEnabled = sSysuiRes!!.getBoolean(sFlagId)
- }
- }
- return monetEnabled
- }
-
- @JvmStatic
- fun toColorString(@ColorInt color: Int): String {
- return String.format("%06X", 0xFFFFFF and color)
- }
-}
diff --git a/src/com/android/customization/model/color/WallpaperColorResources.java b/src/com/android/customization/model/color/WallpaperColorResources.java
deleted file mode 100644
index dc3b9033..00000000
--- a/src/com/android/customization/model/color/WallpaperColorResources.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2022 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.model.color;
-
-import android.app.WallpaperColors;
-import android.content.Context;
-import android.util.SparseIntArray;
-import android.widget.RemoteViews.ColorResources;
-
-import com.android.systemui.monet.ColorScheme;
-import com.android.systemui.monet.TonalPalette;
-
-/** A class to override colors in a {@link Context} with wallpaper colors. */
-public class WallpaperColorResources {
-
- private final SparseIntArray mColorOverlay = new SparseIntArray();
-
- public WallpaperColorResources(WallpaperColors wallpaperColors) {
- ColorScheme wallpaperColorScheme = new ColorScheme(wallpaperColors, /* darkTheme= */ false);
- addOverlayColor(wallpaperColorScheme.getNeutral1(), android.R.color.system_neutral1_10);
- addOverlayColor(wallpaperColorScheme.getNeutral2(), android.R.color.system_neutral2_10);
- addOverlayColor(wallpaperColorScheme.getAccent1(), android.R.color.system_accent1_10);
- addOverlayColor(wallpaperColorScheme.getAccent2(), android.R.color.system_accent2_10);
- addOverlayColor(wallpaperColorScheme.getAccent3(), android.R.color.system_accent3_10);
- }
-
- /** Applies the wallpaper color resources to the {@code context}. */
- public void apply(Context context) {
- ColorResources.create(context, mColorOverlay).apply(context);
- }
-
- private void addOverlayColor(TonalPalette colorSchemehue, int firstResourceColorId) {
- int resourceColorId = firstResourceColorId;
- for (int color : colorSchemehue.getAllShades()) {
- mColorOverlay.put(resourceColorId, color);
- resourceColorId++;
- }
- }
-}
diff --git a/src/com/android/customization/model/grid/GridOption.java b/src/com/android/customization/model/grid/GridOption.java
index 19c5d4f1..a5307c9e 100644
--- a/src/com/android/customization/model/grid/GridOption.java
+++ b/src/com/android/customization/model/grid/GridOption.java
@@ -48,19 +48,19 @@ public class GridOption implements CustomizationOption<GridOption>, Parcelable {
}
};
- private final String mTitle;
- private final boolean mIsCurrent;
private final String mIconShapePath;
private final GridTileDrawable mTileDrawable;
+ public final String title;
public final String name;
public final int rows;
public final int cols;
public final Uri previewImageUri;
public final int previewPagesCount;
+ private boolean mIsCurrent;
public GridOption(String title, String name, boolean isCurrent, int rows, int cols,
Uri previewImageUri, int previewPagesCount, String iconShapePath) {
- mTitle = title;
+ this.title = title;
mIsCurrent = isCurrent;
mIconShapePath = iconShapePath;
mTileDrawable = new GridTileDrawable(rows, cols, mIconShapePath);
@@ -71,8 +71,12 @@ public class GridOption implements CustomizationOption<GridOption>, Parcelable {
this.previewPagesCount = previewPagesCount;
}
+ public void setIsCurrent(boolean isCurrent) {
+ mIsCurrent = isCurrent;
+ }
+
protected GridOption(Parcel in) {
- mTitle = in.readString();
+ title = in.readString();
mIsCurrent = in.readByte() != 0;
mIconShapePath = in.readString();
name = in.readString();
@@ -85,7 +89,7 @@ public class GridOption implements CustomizationOption<GridOption>, Parcelable {
@Override
public String getTitle() {
- return mTitle;
+ return title;
}
@Override
@@ -139,7 +143,7 @@ public class GridOption implements CustomizationOption<GridOption>, Parcelable {
@Override
public void writeToParcel(Parcel parcel, int i) {
- parcel.writeString(mTitle);
+ parcel.writeString(title);
parcel.writeByte((byte) (mIsCurrent ? 1 : 0));
parcel.writeString(mIconShapePath);
parcel.writeString(name);
@@ -154,7 +158,7 @@ public class GridOption implements CustomizationOption<GridOption>, Parcelable {
return String.format(
"GridOption{mTitle='%s', mIsCurrent=%s, mTileDrawable=%s, name='%s', rows=%d, "
+ "cols=%d, previewImageUri=%s, previewPagesCount=%d}\n",
- mTitle, mIsCurrent, mTileDrawable, name, rows, cols, previewImageUri,
+ title, mIsCurrent, mTileDrawable, name, rows, cols, previewImageUri,
previewPagesCount);
}
}
diff --git a/src/com/android/customization/model/grid/GridOptionsManager.java b/src/com/android/customization/model/grid/GridOptionsManager.java
index b7ee37fd..78dbb5b2 100644
--- a/src/com/android/customization/model/grid/GridOptionsManager.java
+++ b/src/com/android/customization/model/grid/GridOptionsManager.java
@@ -99,6 +99,11 @@ public class GridOptionsManager implements CustomizationManager<GridOption> {
}
@Override
+ public void preview(GridOption option) {
+ mProvider.updateView();
+ }
+
+ @Override
public void fetchOptions(OptionsFetchedListener<GridOption> callback, boolean reload) {
sExecutorService.submit(() -> {
List<GridOption> gridOptions = mProvider.fetch(reload);
diff --git a/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java b/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java
index 4e775c62..e71cca9f 100644
--- a/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java
+++ b/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java
@@ -33,6 +33,7 @@ import androidx.lifecycle.MutableLiveData;
import com.android.customization.model.ResourceConstants;
import com.android.wallpaper.R;
+import com.android.wallpaper.config.BaseFlags;
import com.android.wallpaper.util.PreviewUtils;
import java.util.ArrayList;
@@ -57,12 +58,14 @@ public class LauncherGridOptionsProvider {
private final Context mContext;
private final PreviewUtils mPreviewUtils;
+ private final boolean mIsGridApplyButtonEnabled;
private List<GridOption> mOptions;
private OptionChangeLiveData mLiveData;
public LauncherGridOptionsProvider(Context context, String authorityMetadataKey) {
mPreviewUtils = new PreviewUtils(context, authorityMetadataKey);
mContext = context;
+ mIsGridApplyButtonEnabled = BaseFlags.get().isGridApplyButtonEnabled(context);
}
boolean areGridsAvailable() {
@@ -117,9 +120,14 @@ public class LauncherGridOptionsProvider {
mPreviewUtils.renderPreview(bundle, callback);
}
+ void updateView() {
+ mLiveData.postValue(new Object());
+ }
+
int applyGrid(String name) {
ContentValues values = new ContentValues();
values.put("name", name);
+ values.put("enable_apply_button", mIsGridApplyButtonEnabled);
return mContext.getContentResolver().update(mPreviewUtils.getUri(DEFAULT_GRID), values,
null, null);
}
@@ -153,6 +161,14 @@ public class LauncherGridOptionsProvider {
mContentObserver = new ContentObserver(handler) {
@Override
public void onChange(boolean selfChange) {
+ // If grid apply button is enabled, user has previewed the grid before applying
+ // the grid change. Thus there is no need to preview again (which will cause a
+ // blank preview as launcher's is loader thread is busy reloading workspace)
+ // after applying grid change. Thus we should ignore ContentObserver#onChange
+ // from launcher
+ if (BaseFlags.get().isGridApplyButtonEnabled(context.getApplicationContext())) {
+ return;
+ }
postValue(new Object());
}
};
diff --git a/src/com/android/customization/model/grid/data/repository/GridRepository.kt b/src/com/android/customization/model/grid/data/repository/GridRepository.kt
index 9a3be0cc..4379dad5 100644
--- a/src/com/android/customization/model/grid/data/repository/GridRepository.kt
+++ b/src/com/android/customization/model/grid/data/repository/GridRepository.kt
@@ -19,6 +19,7 @@ package com.android.customization.model.grid.data.repository
import androidx.lifecycle.asFlow
import com.android.customization.model.CustomizationManager
+import com.android.customization.model.CustomizationManager.Callback
import com.android.customization.model.grid.GridOption
import com.android.customization.model.grid.GridOptionsManager
import com.android.customization.model.grid.shared.model.GridOptionItemModel
@@ -38,12 +39,17 @@ interface GridRepository {
suspend fun isAvailable(): Boolean
fun getOptionChanges(): Flow<Unit>
suspend fun getOptions(): GridOptionItemsModel
+ fun getSelectedOption(): GridOption?
+ fun applySelectedOption(callback: Callback)
+ fun clearSelectedOption()
+ fun isSelectedOptionApplied(): Boolean
}
class GridRepositoryImpl(
private val applicationScope: CoroutineScope,
private val manager: GridOptionsManager,
private val backgroundDispatcher: CoroutineDispatcher,
+ private val isGridApplyButtonEnabled: Boolean,
) : GridRepository {
override suspend fun isAvailable(): Boolean {
@@ -55,6 +61,10 @@ class GridRepositoryImpl(
private val selectedOption = MutableStateFlow<GridOption?>(null)
+ private var appliedOption: GridOption? = null
+
+ override fun getSelectedOption() = selectedOption.value
+
override suspend fun getOptions(): GridOptionItemsModel {
return withContext(backgroundDispatcher) {
suspendCancellableCoroutine { continuation ->
@@ -62,7 +72,14 @@ class GridRepositoryImpl(
object : CustomizationManager.OptionsFetchedListener<GridOption> {
override fun onOptionsLoaded(options: MutableList<GridOption>?) {
val optionsOrEmpty = options ?: emptyList()
- selectedOption.value = optionsOrEmpty.find { it.isActive(manager) }
+ // After Apply Button is added, we will rely on onSelected() method
+ // to update selectedOption.
+ if (!isGridApplyButtonEnabled || selectedOption.value == null) {
+ selectedOption.value = optionsOrEmpty.find { it.isActive(manager) }
+ }
+ if (isGridApplyButtonEnabled && appliedOption == null) {
+ appliedOption = selectedOption.value
+ }
continuation.resume(
GridOptionItemsModel.Loaded(
optionsOrEmpty.map { option -> toModel(option) }
@@ -105,22 +122,59 @@ class GridRepositoryImpl(
private suspend fun onSelected(option: GridOption) {
withContext(backgroundDispatcher) {
suspendCancellableCoroutine { continuation ->
- manager.apply(
- option,
- object : CustomizationManager.Callback {
- override fun onSuccess() {
- continuation.resume(true)
- }
+ if (isGridApplyButtonEnabled) {
+ selectedOption.value?.setIsCurrent(false)
+ selectedOption.value = option
+ selectedOption.value?.setIsCurrent(true)
+ manager.preview(option)
+ continuation.resume(true)
+ } else {
+ manager.apply(
+ option,
+ object : CustomizationManager.Callback {
+ override fun onSuccess() {
+ continuation.resume(true)
+ }
- override fun onError(throwable: Throwable?) {
- continuation.resume(false)
- }
- },
- )
+ override fun onError(throwable: Throwable?) {
+ continuation.resume(false)
+ }
+ },
+ )
+ }
}
}
}
+ override fun applySelectedOption(callback: Callback) {
+ val option = getSelectedOption()
+ manager.apply(
+ option,
+ if (isGridApplyButtonEnabled) {
+ object : Callback {
+ override fun onSuccess() {
+ callback.onSuccess()
+ appliedOption = option
+ }
+
+ override fun onError(throwable: Throwable?) {
+ callback.onError(throwable)
+ }
+ }
+ } else callback
+ )
+ }
+
+ override fun clearSelectedOption() {
+ if (!isGridApplyButtonEnabled) {
+ return
+ }
+ selectedOption.value?.setIsCurrent(false)
+ selectedOption.value = null
+ }
+
+ override fun isSelectedOptionApplied() = selectedOption.value?.name == appliedOption?.name
+
private fun GridOption?.key(): String? {
return if (this != null) "${cols}x${rows}" else null
}
diff --git a/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt b/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt
index cdb679dd..7abd605b 100644
--- a/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt
+++ b/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt
@@ -17,6 +17,8 @@
package com.android.customization.model.grid.domain.interactor
+import com.android.customization.model.CustomizationManager
+import com.android.customization.model.grid.GridOption
import com.android.customization.model.grid.data.repository.GridRepository
import com.android.customization.model.grid.shared.model.GridOptionItemModel
import com.android.customization.model.grid.shared.model.GridOptionItemsModel
@@ -73,6 +75,16 @@ class GridInteractor(
}
}
+ fun getSelectOptionNonSuspend(): GridOption? = repository.getSelectedOption()
+
+ fun clearSelectedOption() = repository.clearSelectedOption()
+
+ fun isSelectedOptionApplied() = repository.isSelectedOptionApplied()
+
+ fun applySelectedOption(callback: CustomizationManager.Callback) {
+ repository.applySelectedOption(callback)
+ }
+
private suspend fun reload(): GridOptionItemsModel {
val model = repository.getOptions()
return if (model is GridOptionItemsModel.Loaded) {
diff --git a/src/com/android/customization/model/grid/ui/binder/GridScreenBinder.kt b/src/com/android/customization/model/grid/ui/binder/GridScreenBinder.kt
index 78536ca9..56fe425d 100644
--- a/src/com/android/customization/model/grid/ui/binder/GridScreenBinder.kt
+++ b/src/com/android/customization/model/grid/ui/binder/GridScreenBinder.kt
@@ -18,6 +18,7 @@
package com.android.customization.model.grid.ui.binder
import android.view.View
+import android.widget.Button
import android.widget.ImageView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
@@ -41,6 +42,8 @@ object GridScreenBinder {
lifecycleOwner: LifecycleOwner,
backgroundDispatcher: CoroutineDispatcher,
onOptionsChanged: () -> Unit,
+ isGridApplyButtonEnabled: Boolean,
+ onOptionApplied: () -> Unit,
) {
val optionView: RecyclerView = view.requireViewById(R.id.options)
optionView.layoutManager =
@@ -57,8 +60,8 @@ object GridScreenBinder {
backgroundDispatcher = backgroundDispatcher,
foregroundTintSpec =
OptionItemBinder.TintSpec(
- selectedColor = view.context.getColor(R.color.text_color_primary),
- unselectedColor = view.context.getColor(R.color.text_color_secondary),
+ selectedColor = view.context.getColor(R.color.system_on_surface),
+ unselectedColor = view.context.getColor(R.color.system_on_surface),
),
bindIcon = { foregroundView: View, gridIcon: GridIconViewModel ->
val imageView = foregroundView as? ImageView
@@ -67,6 +70,13 @@ object GridScreenBinder {
)
optionView.adapter = adapter
+ if (isGridApplyButtonEnabled) {
+ val applyButton: Button = view.requireViewById(R.id.apply_button)
+ applyButton.visibility = View.VISIBLE
+ view.requireViewById<View>(R.id.apply_button_note).visibility = View.VISIBLE
+ applyButton.setOnClickListener { onOptionApplied() }
+ }
+
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
diff --git a/src/com/android/customization/model/grid/ui/fragment/GridFragment2.kt b/src/com/android/customization/model/grid/ui/fragment/GridFragment2.kt
index 6ab561f3..9e99efee 100644
--- a/src/com/android/customization/model/grid/ui/fragment/GridFragment2.kt
+++ b/src/com/android/customization/model/grid/ui/fragment/GridFragment2.kt
@@ -18,14 +18,24 @@
package com.android.customization.model.grid.ui.fragment
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.Button
+import android.widget.Toast
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
+import androidx.transition.Transition
+import androidx.transition.doOnStart
+import com.android.customization.model.CustomizationManager.Callback
+import com.android.customization.model.grid.domain.interactor.GridInteractor
import com.android.customization.model.grid.ui.binder.GridScreenBinder
import com.android.customization.model.grid.ui.viewmodel.GridScreenViewModel
import com.android.customization.module.ThemePickerInjector
import com.android.wallpaper.R
+import com.android.wallpaper.config.BaseFlags
import com.android.wallpaper.module.CurrentWallpaperInfoFactory
import com.android.wallpaper.module.CustomizationSections
import com.android.wallpaper.module.InjectorProvider
@@ -38,9 +48,13 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
+private val TAG = GridFragment2::class.java.simpleName
+
@OptIn(ExperimentalCoroutinesApi::class)
class GridFragment2 : AppbarFragment() {
+ private lateinit var gridInteractor: GridInteractor
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -54,6 +68,8 @@ class GridFragment2 : AppbarFragment() {
)
setUpToolbar(view)
+ val isGridApplyButtonEnabled = BaseFlags.get().isGridApplyButtonEnabled(requireContext())
+
val injector = InjectorProvider.getInjector() as ThemePickerInjector
val wallpaperInfoFactory = injector.getCurrentWallpaperInfoFactory(requireContext())
@@ -61,10 +77,12 @@ class GridFragment2 : AppbarFragment() {
bindScreenPreview(
view,
wallpaperInfoFactory,
- injector.getWallpaperInteractor(requireContext())
+ injector.getWallpaperInteractor(requireContext()),
+ injector.getGridInteractor(requireContext())
)
val viewModelFactory = injector.getGridScreenViewModelFactory(requireContext())
+ gridInteractor = injector.getGridInteractor(requireContext())
GridScreenBinder.bind(
view = view,
viewModel =
@@ -80,11 +98,50 @@ class GridFragment2 : AppbarFragment() {
bindScreenPreview(
view,
wallpaperInfoFactory,
- injector.getWallpaperInteractor(requireContext())
+ injector.getWallpaperInteractor(requireContext()),
+ gridInteractor,
)
+ if (isGridApplyButtonEnabled) {
+ val applyButton: Button = view.requireViewById(R.id.apply_button)
+ applyButton.isEnabled = !gridInteractor.isSelectedOptionApplied()
+ }
+ },
+ isGridApplyButtonEnabled = isGridApplyButtonEnabled,
+ onOptionApplied = {
+ gridInteractor.applySelectedOption(
+ object : Callback {
+ override fun onSuccess() {
+ Toast.makeText(
+ context,
+ getString(
+ R.string.toast_of_changing_grid,
+ gridInteractor.getSelectOptionNonSuspend()?.title
+ ),
+ Toast.LENGTH_SHORT
+ )
+ .show()
+ val applyButton: Button = view.requireViewById(R.id.apply_button)
+ applyButton.isEnabled = false
+ }
+
+ override fun onError(throwable: Throwable?) {
+ val errorMsg =
+ getString(
+ R.string.toast_of_failure_to_change_grid,
+ gridInteractor.getSelectOptionNonSuspend()?.title
+ )
+ Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show()
+ Log.e(TAG, errorMsg, throwable)
+ }
+ }
+ )
}
)
+ (returnTransition as? Transition)?.doOnStart {
+ view.requireViewById<View>(R.id.preview).isVisible = false
+ }
+
return view
}
@@ -92,10 +149,19 @@ class GridFragment2 : AppbarFragment() {
return getString(R.string.grid_title)
}
+ override fun getToolbarColorId(): Int {
+ return android.R.color.transparent
+ }
+
+ override fun getToolbarTextColor(): Int {
+ return ContextCompat.getColor(requireContext(), R.color.system_on_surface)
+ }
+
private fun bindScreenPreview(
view: View,
wallpaperInfoFactory: CurrentWallpaperInfoFactory,
wallpaperInteractor: WallpaperInteractor,
+ gridInteractor: GridInteractor
): ScreenPreviewBinder.Binding {
return ScreenPreviewBinder.bind(
activity = requireActivity(),
@@ -111,6 +177,11 @@ class GridFragment2 : AppbarFragment() {
R.string.grid_control_metadata_name,
),
),
+ initialExtrasProvider = {
+ val bundle = Bundle()
+ bundle.putString("name", gridInteractor.getSelectOptionNonSuspend()?.name)
+ bundle
+ },
wallpaperInfoProvider = {
suspendCancellableCoroutine { continuation ->
wallpaperInfoFactory.createCurrentWallpaperInfos(
@@ -129,4 +200,11 @@ class GridFragment2 : AppbarFragment() {
onWallpaperPreviewDirty = { activity?.recreate() },
)
}
+
+ override fun onBackPressed(): Boolean {
+ if (BaseFlags.get().isGridApplyButtonEnabled(requireContext())) {
+ gridInteractor.clearSelectedOption()
+ }
+ return super.onBackPressed()
+ }
}
diff --git a/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java b/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java
index d3935e80..4f0cd6c7 100644
--- a/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java
+++ b/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java
@@ -137,13 +137,13 @@ public class ThemeBundledWallpaperInfo extends WallpaperInfo {
@Override
public void showPreview(Activity srcActivity, InlinePreviewIntentFactory factory,
- int requestCode) {
+ int requestCode, boolean isAssetIdPresent) {
try {
- srcActivity.startActivityForResult(factory.newIntent(srcActivity, this), requestCode);
+ srcActivity.startActivityForResult(factory.newIntent(srcActivity, this,
+ isAssetIdPresent), requestCode);
} catch (ActivityNotFoundException |SecurityException e) {
Log.e(TAG, "App isn't installed or ThemePicker doesn't have permission to launch", e);
}
-
}
@Override
diff --git a/src/com/android/customization/module/CustomizationInjector.kt b/src/com/android/customization/module/CustomizationInjector.kt
index 5f8f9d3c..8fd3768d 100644
--- a/src/com/android/customization/module/CustomizationInjector.kt
+++ b/src/com/android/customization/module/CustomizationInjector.kt
@@ -16,6 +16,7 @@
package com.android.customization.module
import android.content.Context
+import android.content.res.Resources
import androidx.activity.ComponentActivity
import androidx.fragment.app.FragmentActivity
import com.android.customization.model.theme.OverlayManagerCompat
@@ -78,5 +79,5 @@ interface CustomizationInjector : Injector {
clockViewFactory: ClockViewFactory,
): ClockSettingsViewModel.Factory
- fun getClockDescriptionUtils(): ClockDescriptionUtils
+ fun getClockDescriptionUtils(resources: Resources): ClockDescriptionUtils
}
diff --git a/src/com/android/customization/module/DefaultCustomizationSections.java b/src/com/android/customization/module/DefaultCustomizationSections.java
index b408a89d..bbe6bef1 100644
--- a/src/com/android/customization/module/DefaultCustomizationSections.java
+++ b/src/com/android/customization/module/DefaultCustomizationSections.java
@@ -8,10 +8,8 @@ import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
-import com.android.customization.model.color.ColorSectionController;
import com.android.customization.model.grid.GridOptionsManager;
import com.android.customization.model.grid.GridSectionController;
-import com.android.customization.model.mode.DarkModeSectionController;
import com.android.customization.model.mode.DarkModeSnapshotRestorer;
import com.android.customization.model.themedicon.ThemedIconSectionController;
import com.android.customization.model.themedicon.ThemedIconSwitchProvider;
@@ -19,6 +17,7 @@ import com.android.customization.model.themedicon.domain.interactor.ThemedIconIn
import com.android.customization.model.themedicon.domain.interactor.ThemedIconSnapshotRestorer;
import com.android.customization.picker.clock.ui.view.ClockViewFactory;
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel;
+import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor;
import com.android.customization.picker.color.ui.section.ColorSectionController2;
import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel;
import com.android.customization.picker.notifications.ui.section.NotificationSectionController;
@@ -35,7 +34,6 @@ import com.android.wallpaper.model.CustomizationSectionController.CustomizationS
import com.android.wallpaper.model.PermissionRequester;
import com.android.wallpaper.model.WallpaperColorsViewModel;
import com.android.wallpaper.model.WallpaperPreviewNavigator;
-import com.android.wallpaper.model.WallpaperSectionController;
import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
import com.android.wallpaper.module.CustomizationSections;
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor;
@@ -61,6 +59,7 @@ public final class DefaultCustomizationSections implements CustomizationSections
private final DarkModeSnapshotRestorer mDarkModeSnapshotRestorer;
private final ThemedIconSnapshotRestorer mThemedIconSnapshotRestorer;
private final ThemedIconInteractor mThemedIconInteractor;
+ private final ColorPickerInteractor mColorPickerInteractor;
public DefaultCustomizationSections(
ColorPickerViewModel.Factory colorPickerViewModelFactory,
@@ -73,7 +72,8 @@ public final class DefaultCustomizationSections implements CustomizationSections
ClockViewFactory clockViewFactory,
DarkModeSnapshotRestorer darkModeSnapshotRestorer,
ThemedIconSnapshotRestorer themedIconSnapshotRestorer,
- ThemedIconInteractor themedIconInteractor) {
+ ThemedIconInteractor themedIconInteractor,
+ ColorPickerInteractor colorPickerInteractor) {
mColorPickerViewModelFactory = colorPickerViewModelFactory;
mKeyguardQuickAffordancePickerInteractor = keyguardQuickAffordancePickerInteractor;
mKeyguardQuickAffordancePickerViewModelFactory =
@@ -85,10 +85,11 @@ public final class DefaultCustomizationSections implements CustomizationSections
mDarkModeSnapshotRestorer = darkModeSnapshotRestorer;
mThemedIconSnapshotRestorer = themedIconSnapshotRestorer;
mThemedIconInteractor = themedIconInteractor;
+ mColorPickerInteractor = colorPickerInteractor;
}
@Override
- public List<CustomizationSectionController<?>> getRevampedUISectionControllersForScreen(
+ public List<CustomizationSectionController<?>> getSectionControllersForScreen(
Screen screen,
FragmentActivity activity,
LifecycleOwner lifecycleOwner,
@@ -121,8 +122,10 @@ public final class DefaultCustomizationSections implements CustomizationSections
sectionNavigationController,
wallpaperInteractor,
mThemedIconInteractor,
+ mColorPickerInteractor,
wallpaperManager,
- isTwoPaneAndSmallWidth)
+ isTwoPaneAndSmallWidth,
+ customizationPickerViewModel)
: new PreviewWithThemeSectionController(
activity,
lifecycleOwner,
@@ -133,8 +136,10 @@ public final class DefaultCustomizationSections implements CustomizationSections
wallpaperPreviewNavigator,
wallpaperInteractor,
mThemedIconInteractor,
+ mColorPickerInteractor,
wallpaperManager,
- isTwoPaneAndSmallWidth));
+ isTwoPaneAndSmallWidth,
+ customizationPickerViewModel));
sectionControllers.add(
new ConnectedSectionController(
@@ -202,57 +207,4 @@ public final class DefaultCustomizationSections implements CustomizationSections
return sectionControllers;
}
-
- @Override
- public List<CustomizationSectionController<?>> getAllSectionControllers(
- FragmentActivity activity,
- LifecycleOwner lifecycleOwner,
- WallpaperColorsViewModel wallpaperColorsViewModel,
- PermissionRequester permissionRequester,
- WallpaperPreviewNavigator wallpaperPreviewNavigator,
- CustomizationSectionNavigationController sectionNavigationController,
- @Nullable Bundle savedInstanceState,
- DisplayUtils displayUtils) {
- List<CustomizationSectionController<?>> sectionControllers = new ArrayList<>();
-
- // Wallpaper section.
- sectionControllers.add(
- new WallpaperSectionController(
- activity,
- lifecycleOwner,
- permissionRequester,
- wallpaperColorsViewModel,
- mThemedIconInteractor.isActivatedAsLiveData(),
- sectionNavigationController,
- wallpaperPreviewNavigator,
- savedInstanceState,
- displayUtils));
-
- // Theme color section.
- sectionControllers.add(new ColorSectionController(
- activity, wallpaperColorsViewModel, lifecycleOwner, savedInstanceState));
-
- // Dark/Light theme section.
- sectionControllers.add(new DarkModeSectionController(
- activity,
- lifecycleOwner.getLifecycle(),
- mDarkModeSnapshotRestorer));
-
- // Themed app icon section.
- sectionControllers.add(new ThemedIconSectionController(
- ThemedIconSwitchProvider.getInstance(activity),
- mThemedIconInteractor,
- savedInstanceState,
- mThemedIconSnapshotRestorer));
-
- // App grid section.
- sectionControllers.add(
- new GridSectionController(
- GridOptionsManager.getInstance(activity),
- sectionNavigationController,
- lifecycleOwner,
- /* isRevampedUiEnabled= */ false));
-
- return sectionControllers;
- }
}
diff --git a/src/com/android/customization/module/StatsLogUserEventLogger.java b/src/com/android/customization/module/StatsLogUserEventLogger.java
index 32e0599b..647cdc9a 100644
--- a/src/com/android/customization/module/StatsLogUserEventLogger.java
+++ b/src/com/android/customization/module/StatsLogUserEventLogger.java
@@ -37,7 +37,6 @@ import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SOURCE_TIPS;
import static com.android.wallpaper.util.LaunchSourceUtils.WALLPAPER_LAUNCH_SOURCE;
import android.app.WallpaperManager;
-import android.content.Context;
import android.content.Intent;
import android.stats.style.StyleEnums;
import android.text.TextUtils;
@@ -47,8 +46,6 @@ import androidx.annotation.Nullable;
import com.android.customization.model.color.ColorOption;
import com.android.customization.model.grid.GridOption;
import com.android.customization.model.theme.ThemeBundle;
-import com.android.wallpaper.module.Injector;
-import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.module.NoOpUserEventLogger;
import com.android.wallpaper.module.WallpaperPreferences;
import com.android.wallpaper.module.WallpaperStatusChecker;
@@ -61,86 +58,59 @@ import java.util.Objects;
*/
public class StatsLogUserEventLogger extends NoOpUserEventLogger implements ThemesUserEventLogger {
- private static final String TAG = "StatsLogUserEventLogger";
- private final Context mContext;
private final WallpaperPreferences mPreferences;
private final WallpaperStatusChecker mWallpaperStatusChecker;
- public StatsLogUserEventLogger(Context appContext) {
- mContext = appContext;
- Injector injector = InjectorProvider.getInjector();
- mPreferences = injector.getPreferences(appContext);
- mWallpaperStatusChecker = injector.getWallpaperStatusChecker();
+ public StatsLogUserEventLogger(
+ WallpaperPreferences preferences,
+ WallpaperStatusChecker wallpaperStatusChecker) {
+ mPreferences = preferences;
+ mWallpaperStatusChecker = wallpaperStatusChecker;
}
@Override
public void logAppLaunched(Intent launchSource) {
- new SysUiStatsLogger()
- .setAction(STYLE_UICHANGED__ACTION__APP_LAUNCHED)
+ new SysUiStatsLogger(STYLE_UICHANGED__ACTION__APP_LAUNCHED)
.setLaunchedPreference(getAppLaunchSource(launchSource))
.log();
}
@Override
public void logResumed(boolean provisioned, boolean wallpaper) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.ONRESUME)
+ new SysUiStatsLogger(StyleEnums.ONRESUME)
.log();
}
@Override
public void logStopped() {
- new SysUiStatsLogger()
- .setAction(StyleEnums.ONSTOP)
+ new SysUiStatsLogger(StyleEnums.ONSTOP)
.log();
}
@Override
public void logActionClicked(String collectionId, int actionLabelResId) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.WALLPAPER_EXPLORE)
+ new SysUiStatsLogger(StyleEnums.WALLPAPER_EXPLORE)
.setWallpaperCategoryHash(getIdHashCode(collectionId))
.log();
}
@Override
public void logIndividualWallpaperSelected(String collectionId) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.WALLPAPER_SELECT)
+ new SysUiStatsLogger(StyleEnums.WALLPAPER_SELECT)
.setWallpaperCategoryHash(getIdHashCode(collectionId))
.log();
}
@Override
public void logCategorySelected(String collectionId) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.WALLPAPER_OPEN_CATEGORY)
+ new SysUiStatsLogger(StyleEnums.WALLPAPER_OPEN_CATEGORY)
.setWallpaperCategoryHash(getIdHashCode(collectionId))
.log();
}
@Override
- public void logLiveWallpaperInfoSelected(String collectionId, @Nullable String wallpaperId) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.LIVE_WALLPAPER_INFO_SELECT)
- .setWallpaperCategoryHash(getIdHashCode(collectionId))
- .setWallpaperIdHash(getIdHashCode(wallpaperId))
- .log();
- }
-
- @Override
- public void logLiveWallpaperCustomizeSelected(String collectionId,
- @Nullable String wallpaperId) {
- new SysUiStatsLogger().setAction(StyleEnums.LIVE_WALLPAPER_CUSTOMIZE_SELECT)
- .setWallpaperCategoryHash(getIdHashCode(collectionId))
- .setWallpaperIdHash(getIdHashCode(wallpaperId))
- .log();
-
- }
-
- @Override
public void logSnapshot() {
- final boolean isLockWallpaperSet = mWallpaperStatusChecker.isLockWallpaperSet(mContext);
+ final boolean isLockWallpaperSet = mWallpaperStatusChecker.isLockWallpaperSet();
final String homeCollectionId = mPreferences.getHomeWallpaperCollectionId();
final String homeRemoteId = mPreferences.getHomeWallpaperRemoteId();
final String effects = mPreferences.getHomeWallpaperEffects();
@@ -151,7 +121,7 @@ public class StatsLogUserEventLogger extends NoOpUserEventLogger implements Them
String lockWallpaperId = isLockWallpaperSet ? mPreferences.getLockWallpaperRemoteId()
: homeWallpaperId;
- new SysUiStatsLogger().setAction(StyleEnums.SNAPSHOT)
+ new SysUiStatsLogger(StyleEnums.SNAPSHOT)
.setWallpaperCategoryHash(getIdHashCode(homeCollectionId))
.setWallpaperIdHash(getIdHashCode(homeWallpaperId))
.setLockWallpaperCategoryHash(getIdHashCode(lockCollectionId))
@@ -167,8 +137,7 @@ public class StatsLogUserEventLogger extends NoOpUserEventLogger implements Them
@Override
public void logWallpaperSet(String collectionId, @Nullable String wallpaperId,
@Nullable String effects) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.WALLPAPER_APPLIED)
+ new SysUiStatsLogger(StyleEnums.WALLPAPER_APPLIED)
.setWallpaperCategoryHash(getIdHashCode(collectionId))
.setWallpaperIdHash(getIdHashCode(wallpaperId))
.setEffectIdHash(getIdHashCode(effects))
@@ -178,8 +147,7 @@ public class StatsLogUserEventLogger extends NoOpUserEventLogger implements Them
@Override
public void logEffectApply(String effect, @EffectStatus int status, long timeElapsedMillis,
int resultCode) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.WALLPAPER_EFFECT_APPLIED)
+ new SysUiStatsLogger(StyleEnums.WALLPAPER_EFFECT_APPLIED)
.setEffectPreference(status)
.setEffectIdHash(getIdHashCode(effect))
.setTimeElapsed(timeElapsedMillis)
@@ -189,10 +157,19 @@ public class StatsLogUserEventLogger extends NoOpUserEventLogger implements Them
@Override
public void logEffectProbe(String effect, @EffectStatus int status) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.WALLPAPER_EFFECT_PROBE)
+ new SysUiStatsLogger(StyleEnums.WALLPAPER_EFFECT_PROBE)
+ .setEffectPreference(status)
+ .setEffectIdHash(getIdHashCode(effect))
+ .log();
+ }
+
+ @Override
+ public void logEffectForegroundDownload(String effect, @EffectStatus int status,
+ long timeElapsedMillis) {
+ new SysUiStatsLogger(StyleEnums.WALLPAPER_EFFECT_FG_DOWNLOAD)
.setEffectPreference(status)
.setEffectIdHash(getIdHashCode(effect))
+ .setTimeElapsed(timeElapsedMillis)
.log();
}
@@ -204,8 +181,7 @@ public class StatsLogUserEventLogger extends NoOpUserEventLogger implements Them
@Override
public void logThemeSelected(ThemeBundle theme, boolean isCustomTheme) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.PICKER_SELECT)
+ new SysUiStatsLogger(StyleEnums.PICKER_SELECT)
.setColorPackageHash(
Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_COLOR)))
.setFontPackageHash(Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_FONT)))
@@ -216,8 +192,7 @@ public class StatsLogUserEventLogger extends NoOpUserEventLogger implements Them
@Override
public void logThemeApplied(ThemeBundle theme, boolean isCustomTheme) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.PICKER_APPLIED)
+ new SysUiStatsLogger(StyleEnums.PICKER_APPLIED)
.setColorPackageHash(
Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_COLOR)))
.setFontPackageHash(Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_FONT)))
@@ -228,8 +203,7 @@ public class StatsLogUserEventLogger extends NoOpUserEventLogger implements Them
@Override
public void logColorApplied(int action, ColorOption colorOption) {
- new SysUiStatsLogger()
- .setAction(action)
+ new SysUiStatsLogger(action)
.setColorPreference(colorOption.getIndex())
.setColorVariant(colorOption.getStyle().ordinal() + 1)
.log();
@@ -237,16 +211,14 @@ public class StatsLogUserEventLogger extends NoOpUserEventLogger implements Them
@Override
public void logGridSelected(GridOption grid) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.PICKER_SELECT)
+ new SysUiStatsLogger(StyleEnums.PICKER_SELECT)
.setLauncherGrid(grid.cols)
.log();
}
@Override
public void logGridApplied(GridOption grid) {
- new SysUiStatsLogger()
- .setAction(StyleEnums.PICKER_APPLIED)
+ new SysUiStatsLogger(StyleEnums.PICKER_APPLIED)
.setLauncherGrid(grid.cols)
.log();
}
diff --git a/src/com/android/customization/module/SysUiStatsLogger.kt b/src/com/android/customization/module/SysUiStatsLogger.kt
index 318bf1ff..8e97b0b6 100644
--- a/src/com/android/customization/module/SysUiStatsLogger.kt
+++ b/src/com/android/customization/module/SysUiStatsLogger.kt
@@ -20,10 +20,8 @@ import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.shared.system.SysUiStatsLog.STYLE_UI_CHANGED
/** The builder for [SysUiStatsLog]. */
-class SysUiStatsLogger {
+class SysUiStatsLogger(val action: Int) {
- private var atom = STYLE_UI_CHANGED
- private var action = StyleEnums.DEFAULT_ACTION
private var colorPackageHash = 0
private var fontPackageHash = 0
private var shapePackageHash = 0
@@ -46,85 +44,83 @@ class SysUiStatsLogger {
private var timeElapsedMillis = 0L
private var effectResultCode = -1
- fun setAction(action: Int) = apply { this.action = action }
-
- fun setColorPackageHash(color_package_hash: Int) = apply {
- this.colorPackageHash = color_package_hash
+ fun setColorPackageHash(colorPackageHash: Int) = apply {
+ this.colorPackageHash = colorPackageHash
}
- fun setFontPackageHash(font_package_hash: Int) = apply {
- this.fontPackageHash = font_package_hash
+ fun setFontPackageHash(fontPackageHash: Int) = apply {
+ this.fontPackageHash = fontPackageHash
}
- fun setShapePackageHash(shape_package_hash: Int) = apply {
- this.shapePackageHash = shape_package_hash
+ fun setShapePackageHash(shapePackageHash: Int) = apply {
+ this.shapePackageHash = shapePackageHash
}
- fun setClockPackageHash(clock_package_hash: Int) = apply {
- this.clockPackageHash = clock_package_hash
+ fun setClockPackageHash(clockPackageHash: Int) = apply {
+ this.clockPackageHash = clockPackageHash
}
- fun setLauncherGrid(launcher_grid: Int) = apply { this.launcherGrid = launcher_grid }
+ fun setLauncherGrid(launcherGrid: Int) = apply { this.launcherGrid = launcherGrid }
- fun setWallpaperCategoryHash(wallpaper_category_hash: Int) = apply {
- this.wallpaperCategoryHash = wallpaper_category_hash
+ fun setWallpaperCategoryHash(wallpaperCategoryHash: Int) = apply {
+ this.wallpaperCategoryHash = wallpaperCategoryHash
}
- fun setWallpaperIdHash(wallpaper_id_hash: Int) = apply {
- this.wallpaperIdHash = wallpaper_id_hash
+ fun setWallpaperIdHash(wallpaperIdHash: Int) = apply {
+ this.wallpaperIdHash = wallpaperIdHash
}
- fun setColorPreference(color_preference: Int) = apply {
- this.colorPreference = color_preference
+ fun setColorPreference(colorPreference: Int) = apply {
+ this.colorPreference = colorPreference
}
- fun setLocationPreference(location_preference: Int) = apply {
- this.locationPreference = location_preference
+ fun setLocationPreference(locationPreference: Int) = apply {
+ this.locationPreference = locationPreference
}
- fun setDatePreference(date_preference: Int) = apply { this.datePreference = date_preference }
+ fun setDatePreference(datePreference: Int) = apply { this.datePreference = datePreference }
- fun setLaunchedPreference(launched_preference: Int) = apply {
- this.launchedPreference = launched_preference
+ fun setLaunchedPreference(launchedPreference: Int) = apply {
+ this.launchedPreference = launchedPreference
}
- fun setEffectPreference(effect_preference: Int) = apply {
- this.effectPreference = effect_preference
+ fun setEffectPreference(effectPreference: Int) = apply {
+ this.effectPreference = effectPreference
}
- fun setEffectIdHash(effect_id_hash: Int) = apply { this.effectIdHash = effect_id_hash }
+ fun setEffectIdHash(effectIdHash: Int) = apply { this.effectIdHash = effectIdHash }
- fun setLockWallpaperCategoryHash(lock_wallpaper_category_hash: Int) = apply {
- this.lockWallpaperCategoryHash = lock_wallpaper_category_hash
+ fun setLockWallpaperCategoryHash(lockWallpaperCategoryHash: Int) = apply {
+ this.lockWallpaperCategoryHash = lockWallpaperCategoryHash
}
- fun setLockWallpaperIdHash(lock_wallpaper_id_hash: Int) = apply {
- this.lockWallpaperIdHash = lock_wallpaper_id_hash
+ fun setLockWallpaperIdHash(lockWallpaperIdHash: Int) = apply {
+ this.lockWallpaperIdHash = lockWallpaperIdHash
}
- fun setFirstLaunchDateSinceSetup(first_launch_date_since_setup: Int) = apply {
- this.firstLaunchDateSinceSetup = first_launch_date_since_setup
+ fun setFirstLaunchDateSinceSetup(firstLaunchDateSinceSetup: Int) = apply {
+ this.firstLaunchDateSinceSetup = firstLaunchDateSinceSetup
}
- fun setFirstWallpaperApplyDateSinceSetup(first_wallpaper_apply_date_since_setup: Int) = apply {
- this.firstWallpaperApplyDateSinceSetup = first_wallpaper_apply_date_since_setup
+ fun setFirstWallpaperApplyDateSinceSetup(firstWallpaperApplyDateSinceSetup: Int) = apply {
+ this.firstWallpaperApplyDateSinceSetup = firstWallpaperApplyDateSinceSetup
}
- fun setAppLaunchCount(app_launch_count: Int) = apply { this.appLaunchCount = app_launch_count }
+ fun setAppLaunchCount(appLaunchCount: Int) = apply { this.appLaunchCount = appLaunchCount }
- fun setColorVariant(color_variant: Int) = apply { this.colorVariant = color_variant }
+ fun setColorVariant(colorVariant: Int) = apply { this.colorVariant = colorVariant }
- fun setTimeElapsed(time_elapsed_millis: Long) = apply {
- this.timeElapsedMillis = time_elapsed_millis
+ fun setTimeElapsed(timeElapsedMillis: Long) = apply {
+ this.timeElapsedMillis = timeElapsedMillis
}
- fun setEffectResultCode(effect_result_code: Int) = apply {
- this.effectResultCode = effect_result_code
+ fun setEffectResultCode(effectResultCode: Int) = apply {
+ this.effectResultCode = effectResultCode
}
fun log() {
SysUiStatsLog.write(
- atom,
+ STYLE_UI_CHANGED,
action,
colorPackageHash,
fontPackageHash,
diff --git a/src/com/android/customization/module/ThemePickerInjector.kt b/src/com/android/customization/module/ThemePickerInjector.kt
index cceb8965..497456f6 100644
--- a/src/com/android/customization/module/ThemePickerInjector.kt
+++ b/src/com/android/customization/module/ThemePickerInjector.kt
@@ -19,17 +19,17 @@ import android.app.UiModeManager
import android.app.WallpaperManager
import android.content.Context
import android.content.Intent
+import android.content.res.Resources
import android.net.Uri
-import android.os.Bundle
import android.text.TextUtils
import androidx.activity.ComponentActivity
-import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import com.android.customization.model.color.ColorCustomizationManager
import com.android.customization.model.color.ColorOptionsProvider
+import com.android.customization.model.color.ColorOptionsProvider.COLOR_SOURCE_PRESET
import com.android.customization.model.grid.GridOptionsManager
import com.android.customization.model.grid.data.repository.GridRepositoryImpl
import com.android.customization.model.grid.domain.interactor.GridInteractor
@@ -68,29 +68,35 @@ import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQui
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
-import com.android.wallpaper.model.LiveWallpaperInfo
+import com.android.wallpaper.config.BaseFlags
+import com.android.wallpaper.dispatchers.BackgroundDispatcher
+import com.android.wallpaper.dispatchers.MainDispatcher
import com.android.wallpaper.model.WallpaperColorsViewModel
-import com.android.wallpaper.model.WallpaperInfo
import com.android.wallpaper.module.CustomizationSections
import com.android.wallpaper.module.FragmentFactory
import com.android.wallpaper.module.UserEventLogger
import com.android.wallpaper.module.WallpaperPicker2Injector
-import com.android.wallpaper.module.WallpaperPreferences
import com.android.wallpaper.picker.CustomizationPickerActivity
-import com.android.wallpaper.picker.ImagePreviewFragment
-import com.android.wallpaper.picker.LivePreviewFragment
-import com.android.wallpaper.picker.PreviewFragment
import com.android.wallpaper.picker.customization.data.content.WallpaperClientImpl
import com.android.wallpaper.picker.customization.data.repository.WallpaperRepository
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
import com.android.wallpaper.util.ScreenSizeCalculator
-import kotlinx.coroutines.Dispatchers
-
-open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInjector {
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+
+@Singleton
+open class ThemePickerInjector
+@Inject
+internal constructor(
+ @MainDispatcher private val mainScope: CoroutineScope,
+ @MainDispatcher private val mainDispatcher: CoroutineDispatcher,
+ @BackgroundDispatcher private val bgDispatcher: CoroutineDispatcher,
+) : WallpaperPicker2Injector(mainScope, bgDispatcher), CustomizationInjector {
private var customizationSections: CustomizationSections? = null
private var userEventLogger: UserEventLogger? = null
- private var prefs: WallpaperPreferences? = null
private var wallpaperInteractor: WallpaperInteractor? = null
private var keyguardQuickAffordancePickerInteractor: KeyguardQuickAffordancePickerInteractor? =
null
@@ -143,6 +149,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
getDarkModeSnapshotRestorer(activity),
getThemedIconSnapshotRestorer(activity),
getThemedIconInteractor(),
+ getColorPickerInteractor(activity, getWallpaperColorsViewModel()),
)
.also { customizationSections = it }
}
@@ -159,38 +166,14 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
return null
}
- override fun getPreviewFragment(
- context: Context,
- wallpaperInfo: WallpaperInfo,
- mode: Int,
- viewAsHome: Boolean,
- viewFullScreen: Boolean,
- testingModeEnabled: Boolean
- ): Fragment {
- return if (wallpaperInfo is LiveWallpaperInfo) LivePreviewFragment()
- else
- ImagePreviewFragment().apply {
- arguments =
- Bundle().apply {
- putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo)
- putInt(PreviewFragment.ARG_PREVIEW_MODE, mode)
- putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome)
- putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen)
- putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled)
- }
- }
- }
-
@Synchronized
override fun getUserEventLogger(context: Context): ThemesUserEventLogger {
- return if (userEventLogger != null) userEventLogger as ThemesUserEventLogger
- else StatsLogUserEventLogger(context.applicationContext).also { userEventLogger = it }
- }
-
- @Synchronized
- override fun getPreferences(context: Context): WallpaperPreferences {
- return prefs
- ?: DefaultCustomizationPreferences(context.applicationContext).also { prefs = it }
+ return userEventLogger as? ThemesUserEventLogger
+ ?: StatsLogUserEventLogger(
+ getPreferences(context.applicationContext),
+ getWallpaperStatusChecker(context.applicationContext),
+ )
+ .also { userEventLogger = it }
}
override fun getFragmentFactory(): FragmentFactory? {
@@ -244,7 +227,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
wallpaperManager = WallpaperManager.getInstance(appContext)
),
wallpaperPreferences = getPreferences(context = appContext),
- backgroundDispatcher = Dispatchers.IO,
+ backgroundDispatcher = bgDispatcher,
),
shouldHandleReload = {
TextUtils.equals(
@@ -284,7 +267,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
val client = getKeyguardQuickAffordancePickerProviderClient(context)
val appContext = context.applicationContext
return KeyguardQuickAffordancePickerInteractor(
- KeyguardQuickAffordancePickerRepository(client, Dispatchers.IO),
+ KeyguardQuickAffordancePickerRepository(client, bgDispatcher),
client
) {
getKeyguardQuickAffordanceSnapshotRestorer(appContext)
@@ -295,7 +278,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
context: Context
): CustomizationProviderClient {
return customizationProviderClient
- ?: CustomizationProviderClientImpl(context.applicationContext, Dispatchers.IO).also {
+ ?: CustomizationProviderClientImpl(context.applicationContext, bgDispatcher).also {
customizationProviderClient = it
}
}
@@ -330,7 +313,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
repository =
NotificationsRepository(
scope = getApplicationCoroutineScope(),
- backgroundDispatcher = Dispatchers.IO,
+ backgroundDispatcher = bgDispatcher,
secureSettingsRepository = getSecureSettingsRepository(context),
),
snapshotRestorer = { getNotificationsSnapshotRestorer(appContext) },
@@ -354,8 +337,8 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
?: ClockRegistryProvider(
context = context.applicationContext,
coroutineScope = getApplicationCoroutineScope(),
- mainDispatcher = Dispatchers.Main,
- backgroundDispatcher = Dispatchers.IO,
+ mainDispatcher = mainDispatcher,
+ backgroundDispatcher = bgDispatcher,
)
.also { clockRegistryProvider = it })
.get()
@@ -372,7 +355,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
secureSettingsRepository = getSecureSettingsRepository(appContext),
registry = getClockRegistry(appContext),
scope = getApplicationCoroutineScope(),
- mainDispatcher = Dispatchers.Main,
+ mainDispatcher = mainDispatcher,
),
snapshotRestorer = { getClockPickerSnapshotRestorer(appContext) },
)
@@ -394,7 +377,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
interactor: ClockPickerInteractor,
): ClockCarouselViewModel.Factory {
return clockCarouselViewModelFactory
- ?: ClockCarouselViewModel.Factory(interactor, Dispatchers.IO).also {
+ ?: ClockCarouselViewModel.Factory(interactor, bgDispatcher).also {
clockCarouselViewModelFactory = it
}
}
@@ -489,7 +472,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
?: DarkModeSnapshotRestorer(
context = appContext,
manager = appContext.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager,
- backgroundDispatcher = Dispatchers.IO,
+ backgroundDispatcher = bgDispatcher,
)
.also { darkModeSnapshotRestorer = it }
}
@@ -537,7 +520,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
.also { clockSettingsViewModelFactory = it }
}
- override fun getClockDescriptionUtils(): ClockDescriptionUtils {
+ override fun getClockDescriptionUtils(resources: Resources): ClockDescriptionUtils {
return clockDescriptionUtils
?: ThemePickerClockDescriptionUtils().also { clockDescriptionUtils = it }
}
@@ -553,9 +536,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
.also { gridScreenViewModelFactory = it }
}
- private fun getGridInteractor(
- context: Context,
- ): GridInteractor {
+ fun getGridInteractor(context: Context): GridInteractor {
val appContext = context.applicationContext
return gridInteractor
?: GridInteractor(
@@ -564,7 +545,9 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
GridRepositoryImpl(
applicationScope = getApplicationCoroutineScope(),
manager = GridOptionsManager.getInstance(context),
- backgroundDispatcher = Dispatchers.IO,
+ backgroundDispatcher = bgDispatcher,
+ isGridApplyButtonEnabled =
+ BaseFlags.get().isGridApplyButtonEnabled(appContext),
),
snapshotRestorer = { getGridSnapshotRestorer(appContext) },
)
@@ -581,6 +564,12 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
.also { gridSnapshotRestorer = it }
}
+ override fun isCurrentSelectedColorPreset(context: Context): Boolean {
+ val colorManager =
+ ColorCustomizationManager.getInstance(context, OverlayManagerCompat(context))
+ return COLOR_SOURCE_PRESET == colorManager.currentColorSource
+ }
+
companion object {
@JvmStatic
private val KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER =
diff --git a/src/com/android/customization/picker/CustomizationPickerApplication.java b/src/com/android/customization/picker/CustomizationPickerApplication.java
deleted file mode 100644
index 178cfbfc..00000000
--- a/src/com/android/customization/picker/CustomizationPickerApplication.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import android.app.Application;
-
-import com.android.customization.module.ThemePickerInjector;
-import com.android.wallpaper.module.InjectorProvider;
-
-public class CustomizationPickerApplication extends Application {
- @Override
- public void onCreate() {
- super.onCreate();
-
- // Initialize the injector.
- InjectorProvider.setInjector(new ThemePickerInjector());
- }
-}
diff --git a/src/com/android/customization/picker/HorizontalTouchMovementAwareNestedScrollView.kt b/src/com/android/customization/picker/HorizontalTouchMovementAwareNestedScrollView.kt
deleted file mode 100644
index 06cf7539..00000000
--- a/src/com/android/customization/picker/HorizontalTouchMovementAwareNestedScrollView.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2023 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
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.MotionEvent
-import android.view.ViewConfiguration
-import androidx.core.widget.NestedScrollView
-import kotlin.math.abs
-
-/**
- * This nested scroll view will detect horizontal touch movements and stop vertical scrolls when a
- * horizontal touch movement is detected.
- */
-class HorizontalTouchMovementAwareNestedScrollView(context: Context, attrs: AttributeSet?) :
- NestedScrollView(context, attrs) {
-
- private var startXPosition = 0f
- private var startYPosition = 0f
- private var isHorizontalTouchMovement = false
-
- override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
- when (event.action) {
- MotionEvent.ACTION_DOWN -> {
- startXPosition = event.x
- startYPosition = event.y
- isHorizontalTouchMovement = false
- }
- MotionEvent.ACTION_MOVE -> {
- val xMoveDistance = abs(event.x - startXPosition)
- val yMoveDistance = abs(event.y - startYPosition)
- if (
- !isHorizontalTouchMovement &&
- xMoveDistance > yMoveDistance &&
- xMoveDistance > ViewConfiguration.get(context).scaledTouchSlop
- ) {
- isHorizontalTouchMovement = true
- }
- }
- else -> {}
- }
- return if (isHorizontalTouchMovement) {
- // We only want to intercept the touch event when the touch moves more vertically than
- // horizontally. So we return false.
- false
- } else {
- super.onInterceptTouchEvent(event)
- }
- }
-}
diff --git a/src/com/android/customization/picker/WallpaperPreviewer.java b/src/com/android/customization/picker/WallpaperPreviewer.java
index 1b9ea9fc..d74bfaea 100644
--- a/src/com/android/customization/picker/WallpaperPreviewer.java
+++ b/src/com/android/customization/picker/WallpaperPreviewer.java
@@ -241,7 +241,7 @@ public class WallpaperPreviewer implements LifecycleObserver {
() -> mFadeInScrim.setVisibility(View.INVISIBLE));
}
}
- }, mWallpaperSurface);
+ }, mWallpaperSurface, WallpaperConnection.WHICH_PREVIEW.PREVIEW_CURRENT);
mWallpaperConnection.setVisibility(true);
mHomePreview.post(() -> {
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
index be6c6cbd..004103f3 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
@@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
@@ -52,11 +53,12 @@ class ClockPickerRepositoryImpl(
override val allClocks: Flow<List<ClockMetadataModel>> =
callbackFlow {
fun send() {
+ val activeClockId = registry.activeClockId
val allClocks =
- registry
- .getClocks()
- .filter { "NOT_IN_USE" !in it.clockId }
- .map { it.toModel() }
+ registry.getClocks().map {
+ it.toModel(isSelected = it.clockId == activeClockId)
+ }
+
trySend(allClocks)
}
@@ -83,13 +85,14 @@ class ClockPickerRepositoryImpl(
override val selectedClock: Flow<ClockMetadataModel> =
callbackFlow {
fun send() {
- val currentClockId = registry.currentClockId
+ val activeClockId = registry.activeClockId
val metadata = registry.settings?.metadata
val model =
registry
.getClocks()
- .find { clockMetadata -> clockMetadata.clockId == currentClockId }
+ .find { clockMetadata -> clockMetadata.clockId == activeClockId }
?.toModel(
+ isSelected = true,
selectedColorId = metadata?.getSelectedColorId(),
colorTone = metadata?.getColorTone()
?: ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS,
@@ -146,9 +149,10 @@ class ClockPickerRepositoryImpl(
)
.map { setting -> setting == 1 }
.map { isDynamic -> if (isDynamic) ClockSize.DYNAMIC else ClockSize.SMALL }
+ .distinctUntilChanged()
.shareIn(
scope = scope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
replay = 1,
)
@@ -176,6 +180,7 @@ class ClockPickerRepositoryImpl(
/** By default, [ClockMetadataModel] has no color information unless specified. */
private fun ClockMetadata.toModel(
+ isSelected: Boolean,
selectedColorId: String? = null,
@IntRange(from = 0, to = 100) colorTone: Int = 0,
@ColorInt seedColor: Int? = null,
@@ -183,6 +188,7 @@ class ClockPickerRepositoryImpl(
return ClockMetadataModel(
clockId = clockId,
name = name,
+ isSelected = isSelected,
selectedColorId = selectedColorId,
colorToneProgress = colorTone,
seedColor = seedColor,
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt b/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt
index 652ffdd2..b197edf9 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt
@@ -29,6 +29,7 @@ import com.android.systemui.shared.plugins.PluginInstance
import com.android.systemui.shared.plugins.PluginManagerImpl
import com.android.systemui.shared.plugins.PluginPrefs
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager_Factory
+import com.android.wallpaper.module.InjectorProvider
import java.util.concurrent.Executors
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -55,6 +56,8 @@ class ClockRegistryProvider(
DefaultClockProvider(context, LayoutInflater.from(context), context.resources),
keepAllLoaded = true,
subTag = "Picker",
+ isTransitClockEnabled =
+ InjectorProvider.getInjector().getFlags().isTransitClockEnabled(context)
)
}
diff --git a/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
index bd87ba6e..25225075 100644
--- a/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
+++ b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
@@ -24,6 +24,7 @@ import androidx.annotation.IntRange
data class ClockMetadataModel(
val clockId: String,
val name: String,
+ val isSelected: Boolean,
val selectedColorId: String?,
@IntRange(from = 0, to = 100) val colorToneProgress: Int,
@ColorInt val seedColor: Int?,
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
index 89fac894..6bd717b7 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -16,8 +16,8 @@
package com.android.customization.picker.clock.ui.binder
import android.content.Context
-import android.view.ViewGroup
-import android.widget.FrameLayout
+import android.content.res.Configuration
+import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
@@ -27,7 +27,6 @@ import androidx.lifecycle.repeatOnLifecycle
import com.android.customization.picker.clock.ui.view.ClockCarouselView
import com.android.customization.picker.clock.ui.view.ClockViewFactory
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
-import com.android.wallpaper.R
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewClickView
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -38,7 +37,6 @@ object ClockCarouselViewBinder {
fun bind(
context: Context,
carouselView: ClockCarouselView,
- singleClockView: ViewGroup,
screenPreviewClickView: ScreenPreviewClickView,
viewModel: ClockCarouselViewModel,
clockViewFactory: ClockViewFactory,
@@ -46,6 +44,7 @@ object ClockCarouselViewBinder {
isTwoPaneAndSmallWidth: Boolean,
) {
carouselView.setClockViewFactory(clockViewFactory)
+ carouselView.isVisible = true
clockViewFactory.updateRegionDarkness()
val carouselAccessibilityDelegate =
CarouselAccessibilityDelegate(
@@ -60,13 +59,12 @@ object ClockCarouselViewBinder {
}
)
screenPreviewClickView.accessibilityDelegate = carouselAccessibilityDelegate
+ screenPreviewClickView.setOnSideClickedListener { isStart ->
+ if (isStart) carouselView.scrollToPrevious() else carouselView.scrollToNext()
+ }
- val singleClockHostView =
- singleClockView.requireViewById<FrameLayout>(R.id.single_clock_host_view)
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch { viewModel.isCarouselVisible.collect { carouselView.isVisible = it } }
-
launch {
combine(viewModel.selectedClockSize, viewModel.allClocks, ::Pair).collect {
(size, allClocks) ->
@@ -100,17 +98,11 @@ object ClockCarouselViewBinder {
}
launch {
- viewModel.isSingleClockViewVisible.collect { singleClockView.isVisible = it }
- }
-
- launch {
- viewModel.clockId.collect { clockId ->
- singleClockHostView.removeAllViews()
- val clockView = clockViewFactory.getLargeView(clockId)
- // The clock view might still be attached to an existing parent. Detach
- // before adding to another parent.
- (clockView.parent as? ViewGroup)?.removeView(clockView)
- singleClockHostView.addView(clockView)
+ val night =
+ (context.resources.configuration.uiMode and
+ Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES)
+ viewModel.getClockCardColorResId(night).collect {
+ carouselView.setCarouselCardColor(ContextCompat.getColor(context, it))
}
}
}
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
index 4f4bd1bb..6e745d54 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
@@ -33,6 +33,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.customization.picker.clock.shared.ClockSize
import com.android.customization.picker.clock.ui.adapter.ClockSettingsTabAdapter
+import com.android.customization.picker.clock.ui.view.ClockCarouselView
import com.android.customization.picker.clock.ui.view.ClockHostView
import com.android.customization.picker.clock.ui.view.ClockSizeRadioButtonGroup
import com.android.customization.picker.clock.ui.view.ClockViewFactory
@@ -199,7 +200,7 @@ object ClockSettingsBinder {
sizeOptions.radioButtonDynamic.isChecked = false
sizeOptions.radioButtonSmall.isChecked = true
clockHostView.doOnPreDraw {
- it.pivotX = 0F
+ it.pivotX = ClockCarouselView.getCenteredHostViewPivotX(it)
it.pivotY = 0F
}
}
diff --git a/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt b/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt
deleted file mode 100644
index f138d6a4..00000000
--- a/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-package com.android.customization.picker.clock.ui.fragment
-
-import android.content.Context
-import android.os.Bundle
-import android.util.TypedValue
-import android.view.ContextThemeWrapper
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.widget.FrameLayout
-import android.widget.TextView
-import android.widget.Toast
-import androidx.core.view.setPadding
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.android.customization.module.ThemePickerInjector
-import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.plugins.ClockMetadata
-import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.wallpaper.R
-import com.android.wallpaper.module.InjectorProvider
-import com.android.wallpaper.picker.AppbarFragment
-
-class ClockCustomDemoFragment : AppbarFragment() {
- @VisibleForTesting lateinit var recyclerView: RecyclerView
- @VisibleForTesting lateinit var clockRegistry: ClockRegistry
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- val view = inflater.inflate(R.layout.fragment_clock_custom_picker_demo, container, false)
- setUpToolbar(view)
- clockRegistry =
- (InjectorProvider.getInjector() as ThemePickerInjector).getClockRegistry(
- requireContext(),
- )
- val listInUse = clockRegistry.getClocks().filter { "NOT_IN_USE" !in it.clockId }
-
- recyclerView = view.requireViewById(R.id.clock_preview_card_list_demo)
- recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
- recyclerView.adapter =
- ClockRecyclerAdapter(listInUse, requireContext()) {
- clockRegistry.currentClockId = it.clockId
- Toast.makeText(context, "${it.name} selected", Toast.LENGTH_SHORT).show()
- }
- return view
- }
-
- override fun getDefaultTitle(): CharSequence {
- return getString(R.string.clock_title)
- }
-
- internal class ClockRecyclerAdapter(
- val list: List<ClockMetadata>,
- val context: Context,
- val onClockSelected: (ClockMetadata) -> Unit
- ) : RecyclerView.Adapter<ClockRecyclerAdapter.ViewHolder>() {
- class ViewHolder(val view: View, val textView: TextView, val onItemClicked: (Int) -> Unit) :
- RecyclerView.ViewHolder(view) {
- init {
- itemView.setOnClickListener { onItemClicked(absoluteAdapterPosition) }
- }
- }
-
- override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
- val rootView = FrameLayout(viewGroup.context)
- val textView =
- TextView(ContextThemeWrapper(viewGroup.context, R.style.SectionTitleTextStyle))
- textView.setPadding(ITEM_PADDING)
- rootView.addView(textView)
- val outValue = TypedValue()
- context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true)
- rootView.setBackgroundResource(outValue.resourceId)
- val lp = RecyclerView.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
- rootView.layoutParams = lp
- return ViewHolder(rootView, textView) { onClockSelected(list[it]) }
- }
-
- override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
- viewHolder.textView.text = list[position].name
- }
-
- override fun getItemCount() = list.size
-
- companion object {
- val ITEM_PADDING = 40
- }
- }
-}
diff --git a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
index f4684d88..4805f376 100644
--- a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
+++ b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
@@ -20,8 +20,12 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.cardview.widget.CardView
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.get
+import androidx.transition.Transition
+import androidx.transition.doOnStart
import com.android.customization.module.ThemePickerInjector
import com.android.customization.picker.clock.ui.binder.ClockSettingsBinder
import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
@@ -130,6 +134,8 @@ class ClockSettingsFragment : AppbarFragment() {
viewLifecycleOwner,
)
+ (returnTransition as? Transition)?.doOnStart { lockScreenView.isVisible = false }
+
return view
}
@@ -140,4 +146,8 @@ class ClockSettingsFragment : AppbarFragment() {
override fun getToolbarColorId(): Int {
return android.R.color.transparent
}
+
+ override fun getToolbarTextColor(): Int {
+ return ContextCompat.getColor(requireContext(), R.color.system_on_surface)
+ }
}
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
index 8764e541..d4f501b7 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -16,6 +16,7 @@
package com.android.customization.picker.clock.ui.view
import android.content.Context
+import android.content.res.ColorStateList
import android.content.res.Resources
import android.util.AttributeSet
import android.view.LayoutInflater
@@ -71,17 +72,54 @@ class ClockCarouselView(
clockViewFactory = factory
}
+ // This function is for the custom accessibility action to trigger a transition to the next
+ // carousel item. If the current item is the last item in the carousel, the next item
+ // will be the first item.
fun transitionToNext() {
- val index = (carousel.currentIndex + 1) % carousel.count
- if (index < carousel.count && index > 0) {
- carousel.transitionToIndex(index, 0)
+ if (carousel.count != 0) {
+ val index = (carousel.currentIndex + 1) % carousel.count
+ carousel.jumpToIndex(index)
+ // Explicitly called this since using transitionToIndex(index) leads to
+ // race-condition between announcement of content description of the correct clock-face
+ // and the selection of clock face itself
+ adapter.onNewItem(index)
}
}
+ // This function is for the custom accessibility action to trigger a transition to
+ // the previous carousel item. If the current item is the first item in the carousel,
+ // the previous item will be the last item.
fun transitionToPrevious() {
- val index = (carousel.currentIndex - 1) % carousel.count
- if (index < carousel.count && index > 0) {
- carousel.transitionToIndex(index, 0)
+ if (carousel.count != 0) {
+ val index = (carousel.currentIndex + carousel.count - 1) % carousel.count
+ carousel.jumpToIndex(index)
+ // Explicitly called this since using transitionToIndex(index) leads to
+ // race-condition between announcement of content description of the correct clock-face
+ // and the selection of clock face itself
+ adapter.onNewItem(index)
+ }
+ }
+
+ fun scrollToNext() {
+ if (
+ carousel.count <= 1 ||
+ (!carousel.isInfinite && carousel.currentIndex == carousel.count - 1)
+ ) {
+ // No need to scroll if the count is equal or less than 1
+ return
+ }
+ if (motionLayout.currentState == R.id.start) {
+ motionLayout.transitionToState(R.id.next, TRANSITION_DURATION)
+ }
+ }
+
+ fun scrollToPrevious() {
+ if (carousel.count <= 1 || (!carousel.isInfinite && carousel.currentIndex == 0)) {
+ // No need to scroll if the count is equal or less than 1
+ return
+ }
+ if (motionLayout.currentState == R.id.start) {
+ motionLayout.transitionToState(R.id.previous, TRANSITION_DURATION)
}
}
@@ -100,8 +138,15 @@ class ClockCarouselView(
}
adapter = ClockCarouselAdapter(clockSize, clocks, clockViewFactory, onClockSelected)
+ carousel.isInfinite = clocks.size >= MIN_CLOCKS_TO_ENABLE_INFINITE_CAROUSEL
carousel.setAdapter(adapter)
- carousel.refresh()
+ val indexOfSelectedClock =
+ clocks
+ .indexOfFirst { it.isSelected }
+ // If not found, default to the first clock as selected:
+ .takeIf { it != -1 }
+ ?: 0
+ carousel.jumpToIndex(indexOfSelectedClock)
motionLayout.setTransitionListener(
object : MotionLayout.TransitionListener {
@@ -211,11 +256,13 @@ class ClockCarouselView(
}
?: return
offCenterClockHostView.doOnPreDraw {
- it.pivotX = progress * it.width / 2
+ it.pivotX =
+ progress * it.width / 2 + (1 - progress) * getCenteredHostViewPivotX(it)
it.pivotY = progress * it.height / 2
}
toCenterClockHostView.doOnPreDraw {
- it.pivotX = (1 - progress) * it.width / 2
+ it.pivotX =
+ (1 - progress) * it.width / 2 + progress * getCenteredHostViewPivotX(it)
it.pivotY = (1 - progress) * it.height / 2
}
offCenterClockFrame.translationX =
@@ -265,13 +312,25 @@ class ClockCarouselView(
fun setSelectedClockIndex(
index: Int,
) {
- // jumpToIndex to the same position can cause the views unnecessarily populate again.
- // Only call jumpToIndex when the jump-to index is different from the current carousel.
- if (index != carousel.currentIndex) {
+ // 1. setUpClockCarouselView() can possibly not be called before setSelectedClockIndex().
+ // We need to check if index out of bound.
+ // 2. jumpToIndex() to the same position can cause the views unnecessarily populate again.
+ // We only call jumpToIndex when the index is different from the current carousel.
+ if (index < carousel.count && index != carousel.currentIndex) {
carousel.jumpToIndex(index)
}
}
+ fun setCarouselCardColor(color: Int) {
+ itemViewIds.forEach { id ->
+ val cardViewId = getClockCardViewId(id)
+ cardViewId?.let {
+ val cardView = motionLayout.requireViewById<View>(it)
+ cardView.backgroundTintList = ColorStateList.valueOf(color)
+ }
+ }
+ }
+
private fun overrideScreenPreviewWidth() {
val overrideWidth =
context.resources.getDimensionPixelSize(
@@ -417,7 +476,7 @@ class ClockCarouselView(
) {
clockHostView.doOnPreDraw {
if (isMiddleView) {
- it.pivotX = 0F
+ it.pivotX = getCenteredHostViewPivotX(it)
it.pivotY = 0F
clockView.translationX = 0F
clockView.translationY = 0F
@@ -446,7 +505,10 @@ class ClockCarouselView(
}
companion object {
+ // The carousel needs to have at least 5 different clock faces to be infinite
+ const val MIN_CLOCKS_TO_ENABLE_INFINITE_CAROUSEL = 5
const val CLOCK_CAROUSEL_VIEW_SCALE = 0.5f
+ const val TRANSITION_DURATION = 250
val itemViewIds =
listOf(
@@ -507,6 +569,10 @@ class ClockCarouselView(
return rootViewId == R.id.item_view_2
}
+ fun getCenteredHostViewPivotX(hostView: View): Float {
+ return if (hostView.isLayoutRtl) hostView.width.toFloat() else 0F
+ }
+
private fun getTranslationDistance(
hostLength: Int,
frameLength: Int,
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt
index 708fa2f9..98114260 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt
@@ -20,15 +20,14 @@ import com.android.customization.module.CustomizationInjector
import com.android.wallpaper.R
import com.android.wallpaper.module.InjectorProvider
-class ClockCarouselItemViewModel(val clockId: String) {
+class ClockCarouselItemViewModel(val clockId: String, val isSelected: Boolean) {
/** Description for accessibility purposes when a clock is selected. */
fun getContentDescription(resources: Resources): String {
val clockContent =
(InjectorProvider.getInjector() as? CustomizationInjector)
- ?.getClockDescriptionUtils()
- ?.getDescriptionResId(clockId)
- ?.let { resources.getString(it) }
+ ?.getClockDescriptionUtils(resources)
+ ?.getDescription(clockId)
?: ""
return resources.getString(R.string.select_clock_action_description, clockContent)
}
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
index a4f9cc4a..27c25a20 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
@@ -15,11 +15,13 @@
*/
package com.android.customization.picker.clock.ui.viewmodel
+import android.graphics.Color
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
import com.android.customization.picker.clock.shared.ClockSize
+import com.android.wallpaper.R
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
@@ -27,7 +29,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
@@ -39,8 +40,7 @@ import kotlinx.coroutines.launch
* Clock carousel view model that provides data for the carousel of clock previews. When there is
* only one item, we should show a single clock preview instead of a carousel.
*/
-class ClockCarouselViewModel
-constructor(
+class ClockCarouselViewModel(
private val interactor: ClockPickerInteractor,
private val backgroundDispatcher: CoroutineDispatcher,
) : ViewModel() {
@@ -50,7 +50,7 @@ constructor(
.mapLatest { allClocks ->
// Delay to avoid the case that the full list of clocks is not initiated.
delay(CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
- allClocks.map { ClockCarouselItemViewModel(it.clockId) }
+ allClocks.map { ClockCarouselItemViewModel(it.clockId, it.isSelected) }
}
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
@@ -58,7 +58,37 @@ constructor(
val seedColor: Flow<Int?> = interactor.seedColor
- val isCarouselVisible: Flow<Boolean> = allClocks.map { it.size > 1 }.distinctUntilChanged()
+ fun getClockCardColorResId(isDarkThemeEnabled: Boolean): Flow<Int> {
+ return interactor.seedColor.map {
+ it.let { seedColor ->
+ // if seedColor is null, default clock color is selected
+ if (seedColor == null) {
+ if (isDarkThemeEnabled) {
+ // In dark mode, use darkest surface container color
+ R.color.system_surface_container_high
+ } else {
+ // In light mode, use lightest surface container color
+ R.color.system_surface_bright
+ }
+ } else {
+ val luminance = Color.luminance(seedColor)
+ if (isDarkThemeEnabled) {
+ if (luminance <= CARD_COLOR_CHANGE_LUMINANCE_THRESHOLD_DARK_THEME) {
+ R.color.system_surface_bright
+ } else {
+ R.color.system_surface_container_high
+ }
+ } else {
+ if (luminance <= CARD_COLOR_CHANGE_LUMINANCE_THRESHOLD_LIGHT_THEME) {
+ R.color.system_surface_bright
+ } else {
+ R.color.system_surface_container_highest
+ }
+ }
+ }
+ }
+ }
+ }
@OptIn(ExperimentalCoroutinesApi::class)
val selectedIndex: Flow<Int> =
@@ -77,15 +107,6 @@ constructor(
}
.mapNotNull { it }
- // Handle the case when there is only one clock in the carousel
- val isSingleClockViewVisible: Flow<Boolean> =
- allClocks.map { it.size == 1 }.distinctUntilChanged()
-
- val clockId: Flow<String> =
- allClocks
- .map { allClockIds -> if (allClockIds.size == 1) allClockIds[0].clockId else null }
- .mapNotNull { it }
-
private var setSelectedClockJob: Job? = null
fun setSelectedClock(clockId: String) {
setSelectedClockJob?.cancel()
@@ -109,5 +130,7 @@ constructor(
companion object {
const val CLOCKS_EVENT_UPDATE_DELAY_MILLIS: Long = 100
+ const val CARD_COLOR_CHANGE_LUMINANCE_THRESHOLD_LIGHT_THEME: Float = 0.85f
+ const val CARD_COLOR_CHANGE_LUMINANCE_THRESHOLD_DARK_THEME: Float = 0.03f
}
}
diff --git a/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt b/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt
index 9a0b66f1..28ea4a3f 100644
--- a/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt
+++ b/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt
@@ -15,14 +15,12 @@
*/
package com.android.customization.picker.clock.utils
-import androidx.annotation.StringRes
-
/** Provides clock description for accessibility purposes. */
interface ClockDescriptionUtils {
/**
- * TODO (b/287507746) : Migrate description res ID to system UI or a shared library, instead of
- * preserving the clock description at the Wallpaper Picker side.
+ * TODO (b/287507746) : Migrate the clock description to system UI or a shared library, instead
+ * of preserving at the Wallpaper Picker side.
*/
- @StringRes fun getDescriptionResId(clockId: String): Int
+ fun getDescription(clockId: String): String
}
diff --git a/src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt b/src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt
index 43f19b39..a04ebfff 100644
--- a/src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt
+++ b/src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt
@@ -15,12 +15,8 @@
*/
package com.android.customization.picker.clock.utils
-import androidx.annotation.StringRes
-import com.android.wallpaper.R
-
class ThemePickerClockDescriptionUtils : ClockDescriptionUtils {
- @StringRes
- override fun getDescriptionResId(clockId: String): Int {
- return R.string.clock_title
+ override fun getDescription(clockId: String): String {
+ return ""
}
}
diff --git a/src/com/android/customization/picker/color/ColorSectionView.java b/src/com/android/customization/picker/color/ColorSectionView.java
deleted file mode 100644
index b8ba2e4e..00000000
--- a/src/com/android/customization/picker/color/ColorSectionView.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.customization.picker.color;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import androidx.annotation.Nullable;
-
-import com.android.wallpaper.picker.SectionView;
-
-/**
- * The class inherits from {@link SectionView} as the view representing the color section of the
- * customization picker.
- */
-public final class ColorSectionView extends SectionView {
- public ColorSectionView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-}
diff --git a/src/com/android/customization/picker/color/data/repository/ColorPickerRepository.kt b/src/com/android/customization/picker/color/data/repository/ColorPickerRepository.kt
index 7cf9fd03..fccaa658 100644
--- a/src/com/android/customization/picker/color/data/repository/ColorPickerRepository.kt
+++ b/src/com/android/customization/picker/color/data/repository/ColorPickerRepository.kt
@@ -19,12 +19,15 @@ package com.android.customization.picker.color.data.repository
import com.android.customization.picker.color.shared.model.ColorOptionModel
import com.android.customization.picker.color.shared.model.ColorType
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
/**
* Abstracts access to application state related to functionality for selecting, picking, or setting
* system color.
*/
interface ColorPickerRepository {
+ /** Whether the system color is in the process of being updated */
+ val isApplyingSystemColor: StateFlow<Boolean>
/** List of wallpaper and preset color options on the device, categorized by Color Type */
val colorOptions: Flow<Map<ColorType, List<ColorOptionModel>>>
diff --git a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
index 41ef3a57..6540ce06 100644
--- a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
@@ -27,7 +27,9 @@ import com.android.systemui.monet.Style
import com.android.wallpaper.model.WallpaperColorsModel
import com.android.wallpaper.model.WallpaperColorsViewModel
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -43,7 +45,13 @@ class ColorPickerRepositoryImpl(
wallpaperColorsViewModel.homeWallpaperColors
private val lockWallpaperColors: StateFlow<WallpaperColorsModel?> =
wallpaperColorsViewModel.lockWallpaperColors
+ private var selectedColorOption: MutableStateFlow<ColorOptionModel> =
+ MutableStateFlow(getCurrentColorOption())
+ private val _isApplyingSystemColor = MutableStateFlow(false)
+ override val isApplyingSystemColor = _isApplyingSystemColor.asStateFlow()
+
+ // TODO (b/299510645): update color options on selected option change after restart is disabled
override val colorOptions: Flow<Map<ColorType, List<ColorOptionModel>>> =
combine(homeWallpaperColors, lockWallpaperColors) { homeColors, lockColors ->
homeColors to lockColors
@@ -109,17 +117,21 @@ class ColorPickerRepositoryImpl(
}
}
- override suspend fun select(colorOptionModel: ColorOptionModel) =
+ override suspend fun select(colorOptionModel: ColorOptionModel) {
+ _isApplyingSystemColor.value = true
suspendCancellableCoroutine { continuation ->
colorManager.apply(
colorOptionModel.colorOption,
object : CustomizationManager.Callback {
override fun onSuccess() {
+ _isApplyingSystemColor.value = false
+ selectedColorOption.value = colorOptionModel
continuation.resumeWith(Result.success(Unit))
}
override fun onError(throwable: Throwable?) {
Log.w(TAG, "Apply theme with error", throwable)
+ _isApplyingSystemColor.value = false
continuation.resumeWith(
Result.failure(throwable ?: Throwable("Error loading theme bundles"))
)
@@ -127,6 +139,7 @@ class ColorPickerRepositoryImpl(
}
)
}
+ }
override fun getCurrentColorOption(): ColorOptionModel {
val overlays = colorManager.currentOverlays
diff --git a/src/com/android/customization/picker/color/data/repository/FakeColorPickerRepository.kt b/src/com/android/customization/picker/color/data/repository/FakeColorPickerRepository.kt
index 714129df..bb2ef9d3 100644
--- a/src/com/android/customization/picker/color/data/repository/FakeColorPickerRepository.kt
+++ b/src/com/android/customization/picker/color/data/repository/FakeColorPickerRepository.kt
@@ -29,6 +29,9 @@ import kotlinx.coroutines.flow.asStateFlow
class FakeColorPickerRepository(private val context: Context) : ColorPickerRepository {
+ private val _isApplyingSystemColor = MutableStateFlow(false)
+ override val isApplyingSystemColor = _isApplyingSystemColor.asStateFlow()
+
private lateinit var selectedColorOption: ColorOptionModel
private val _colorOptions =
diff --git a/src/com/android/customization/picker/color/domain/interactor/ColorPickerInteractor.kt b/src/com/android/customization/picker/color/domain/interactor/ColorPickerInteractor.kt
index 8c7a4b72..d3b2ebad 100644
--- a/src/com/android/customization/picker/color/domain/interactor/ColorPickerInteractor.kt
+++ b/src/com/android/customization/picker/color/domain/interactor/ColorPickerInteractor.kt
@@ -26,6 +26,8 @@ class ColorPickerInteractor(
private val repository: ColorPickerRepository,
private val snapshotRestorer: Provider<ColorPickerSnapshotRestorer>,
) {
+ val isApplyingSystemColor = repository.isApplyingSystemColor
+
/**
* The newly selected color option for overwriting the current active option during an
* optimistic update, the value is set to null when update fails
diff --git a/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt b/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
index cd9dd540..0f82f494 100644
--- a/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
+++ b/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
@@ -93,29 +93,21 @@ object ColorPickerBinder {
launch {
viewModel.colorOptions.collect { colorOptions ->
- colorOptionAdapter.setItems(colorOptions)
- // the same recycler view is used for different color types tabs
- // the scroll state of each tab should be independent of others
- if (layoutManagerSavedState != null) {
- colorOptionContainerView.post {
+ // only set or restore instance state on a recycler view once data binding
+ // is complete to ensure scroll position is reflected correctly
+ colorOptionAdapter.setItems(colorOptions) {
+ // the same recycler view is used for different color types tabs
+ // the scroll state of each tab should be independent of others
+ if (layoutManagerSavedState != null) {
(colorOptionContainerView.layoutManager as LinearLayoutManager)
.onRestoreInstanceState(layoutManagerSavedState)
layoutManagerSavedState = null
+ } else {
+ var indexToFocus = colorOptions.indexOfFirst { it.isSelected.value }
+ indexToFocus = if (indexToFocus < 0) 0 else indexToFocus
+ (colorOptionContainerView.layoutManager as LinearLayoutManager)
+ .scrollToPositionWithOffset(indexToFocus, 0)
}
- } else {
- var indexToFocus = colorOptions.indexOfFirst { it.isSelected.value }
- indexToFocus = if (indexToFocus < 0) 0 else indexToFocus
- val linearLayoutManager =
- object : LinearLayoutManager(view.context, HORIZONTAL, false) {
- override fun onLayoutCompleted(state: RecyclerView.State?) {
- super.onLayoutCompleted(state)
- // scrollToPosition seems to be inconsistently moving
- // selected
- // color to different positions
- scrollToPositionWithOffset(indexToFocus, 0)
- }
- }
- colorOptionContainerView.layoutManager = linearLayoutManager
}
}
}
@@ -123,9 +115,13 @@ object ColorPickerBinder {
}
return object : Binding {
override fun saveInstanceState(savedState: Bundle) {
+ // as a workaround for the picker restarting twice after a config change, if the
+ // picker restarts before the saved state was applied and set to null,
+ // re-use the same saved state
savedState.putParcelable(
LAYOUT_MANAGER_SAVED_STATE,
- colorOptionContainerView.layoutManager?.onSaveInstanceState()
+ layoutManagerSavedState
+ ?: colorOptionContainerView.layoutManager?.onSaveInstanceState()
)
}
diff --git a/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt b/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
index 2daefe47..ad816143 100644
--- a/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
+++ b/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
@@ -112,22 +112,20 @@ object ColorSectionViewBinder {
val optionSelectedView = itemView.requireViewById<ImageView>(R.id.option_selected)
lifecycleOwner.lifecycleScope.launch {
- lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- item.isSelected.collect { isSelected ->
- optionSelectedView.isVisible = isSelected
- }
+ launch {
+ item.isSelected.collect { isSelected ->
+ optionSelectedView.isVisible = isSelected
}
- launch {
- item.onClicked.collect { onClicked ->
- itemView.setOnClickListener(
- if (onClicked != null) {
- View.OnClickListener { onClicked.invoke() }
- } else {
- null
- }
- )
- }
+ }
+ launch {
+ item.onClicked.collect { onClicked ->
+ itemView.setOnClickListener(
+ if (onClicked != null) {
+ View.OnClickListener { onClicked.invoke() }
+ } else {
+ null
+ }
+ )
}
}
}
diff --git a/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt b/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt
index 78bfa43e..4ef29d6e 100644
--- a/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt
+++ b/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt
@@ -22,9 +22,13 @@ import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.cardview.widget.CardView
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.get
import androidx.lifecycle.lifecycleScope
+import androidx.transition.Transition
+import androidx.transition.doOnStart
import com.android.customization.model.mode.DarkModeSectionController
import com.android.customization.module.ThemePickerInjector
import com.android.customization.picker.color.ui.binder.ColorPickerBinder
@@ -92,53 +96,58 @@ class ColorPickerFragment : AppbarFragment() {
savedInstanceState?.let { binding?.restoreInstanceState(it) }
- ScreenPreviewBinder.bind(
- activity = requireActivity(),
- previewView = lockScreenView,
- viewModel =
- ScreenPreviewViewModel(
- previewUtils =
- PreviewUtils(
- context = requireContext(),
- authority =
- requireContext()
- .getString(
- R.string.lock_screen_preview_provider_authority,
- ),
- ),
- wallpaperInfoProvider = { forceReload ->
- suspendCancellableCoroutine { continuation ->
- wallpaperInfoFactory.createCurrentWallpaperInfos(
- { homeWallpaper, lockWallpaper, _ ->
- lifecycleScope.launch {
- if (
- wcViewModel.lockWallpaperColors.value
- is WallpaperColorsModel.Loading
- ) {
- loadInitialColors(
- wallpaperManager,
- wcViewModel,
- CustomizationSections.Screen.LOCK_SCREEN
- )
+ val lockScreenPreviewBinder =
+ ScreenPreviewBinder.bind(
+ activity = requireActivity(),
+ previewView = lockScreenView,
+ viewModel =
+ ScreenPreviewViewModel(
+ previewUtils =
+ PreviewUtils(
+ context = requireContext(),
+ authority =
+ requireContext()
+ .getString(
+ R.string.lock_screen_preview_provider_authority,
+ ),
+ ),
+ wallpaperInfoProvider = { forceReload ->
+ suspendCancellableCoroutine { continuation ->
+ wallpaperInfoFactory.createCurrentWallpaperInfos(
+ { homeWallpaper, lockWallpaper, _ ->
+ lifecycleScope.launch {
+ if (
+ wcViewModel.lockWallpaperColors.value
+ is WallpaperColorsModel.Loading
+ ) {
+ loadInitialColors(
+ wallpaperManager,
+ wcViewModel,
+ CustomizationSections.Screen.LOCK_SCREEN
+ )
+ }
}
- }
- continuation.resume(lockWallpaper ?: homeWallpaper, null)
- },
- forceReload,
- )
- }
- },
- onWallpaperColorChanged = { colors ->
- wcViewModel.setLockWallpaperColors(colors)
- },
- wallpaperInteractor = injector.getWallpaperInteractor(requireContext()),
- screen = CustomizationSections.Screen.LOCK_SCREEN,
- ),
- lifecycleOwner = this,
- offsetToStart =
- displayUtils.isSingleDisplayOrUnfoldedHorizontalHinge(requireActivity()),
- onWallpaperPreviewDirty = { activity?.recreate() },
- )
+ continuation.resume(lockWallpaper ?: homeWallpaper, null)
+ },
+ forceReload,
+ )
+ }
+ },
+ onWallpaperColorChanged = { colors ->
+ wcViewModel.setLockWallpaperColors(colors)
+ },
+ wallpaperInteractor = injector.getWallpaperInteractor(requireContext()),
+ screen = CustomizationSections.Screen.LOCK_SCREEN,
+ ),
+ lifecycleOwner = this,
+ offsetToStart =
+ displayUtils.isSingleDisplayOrUnfoldedHorizontalHinge(requireActivity()),
+ onWallpaperPreviewDirty = { activity?.recreate() },
+ )
+ val shouldMirrorHomePreview =
+ wallpaperManager.getWallpaperInfo(WallpaperManager.FLAG_SYSTEM) != null &&
+ wallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0
+ val mirrorSurface = if (shouldMirrorHomePreview) lockScreenPreviewBinder.surface() else null
ScreenPreviewBinder.bind(
activity = requireActivity(),
previewView = homeScreenView,
@@ -185,6 +194,7 @@ class ColorPickerFragment : AppbarFragment() {
offsetToStart =
displayUtils.isSingleDisplayOrUnfoldedHorizontalHinge(requireActivity()),
onWallpaperPreviewDirty = { activity?.recreate() },
+ mirrorSurface = mirrorSurface,
)
val darkModeToggleContainerView: FrameLayout =
view.requireViewById(R.id.dark_mode_toggle_container)
@@ -197,6 +207,12 @@ class ColorPickerFragment : AppbarFragment() {
.createView(requireContext())
darkModeSectionView.background = null
darkModeToggleContainerView.addView(darkModeSectionView)
+
+ (returnTransition as? Transition)?.doOnStart {
+ lockScreenView.isVisible = false
+ homeScreenView.isVisible = false
+ }
+
return view
}
@@ -236,4 +252,8 @@ class ColorPickerFragment : AppbarFragment() {
override fun getToolbarColorId(): Int {
return android.R.color.transparent
}
+
+ override fun getToolbarTextColor(): Int {
+ return ContextCompat.getColor(requireContext(), R.color.system_on_surface)
+ }
}
diff --git a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
index a828f837..71dfe1da 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
@@ -23,7 +23,6 @@ import android.graphics.Rect
import android.view.TouchDelegate
import android.view.View
import android.view.View.OnAttachStateChangeListener
-import android.view.ViewGroup
import android.view.ViewStub
import androidx.activity.ComponentActivity
import androidx.constraintlayout.helper.widget.Carousel
@@ -39,7 +38,9 @@ import com.android.customization.picker.clock.ui.fragment.ClockSettingsFragment
import com.android.customization.picker.clock.ui.view.ClockCarouselView
import com.android.customization.picker.clock.ui.view.ClockViewFactory
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
+import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
import com.android.wallpaper.R
+import com.android.wallpaper.model.CustomizationSectionController
import com.android.wallpaper.model.CustomizationSectionController.CustomizationSectionNavigationController
import com.android.wallpaper.model.WallpaperColorsViewModel
import com.android.wallpaper.model.WallpaperPreviewNavigator
@@ -49,6 +50,7 @@ import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInt
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewClickView
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewSectionController
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewView
+import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel
import com.android.wallpaper.util.DisplayUtils
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@@ -70,8 +72,10 @@ class PreviewWithClockCarouselSectionController(
private val navigationController: CustomizationSectionNavigationController,
wallpaperInteractor: WallpaperInteractor,
themedIconInteractor: ThemedIconInteractor,
+ colorPickerInteractor: ColorPickerInteractor,
wallpaperManager: WallpaperManager,
private val isTwoPaneAndSmallWidth: Boolean,
+ customizationPickerViewModel: CustomizationPickerViewModel,
) :
PreviewWithThemeSectionController(
activity,
@@ -83,8 +87,10 @@ class PreviewWithClockCarouselSectionController(
wallpaperPreviewNavigator,
wallpaperInteractor,
themedIconInteractor,
+ colorPickerInteractor,
wallpaperManager,
isTwoPaneAndSmallWidth,
+ customizationPickerViewModel,
) {
private val viewModel =
@@ -98,8 +104,11 @@ class PreviewWithClockCarouselSectionController(
override val hideLockScreenClockPreview = true
- override fun createView(context: Context): ScreenPreviewView {
- val view = super.createView(context)
+ override fun createView(
+ context: Context,
+ params: CustomizationSectionController.ViewCreationParams,
+ ): ScreenPreviewView {
+ val view = super.createView(context, params)
if (screen == CustomizationSections.Screen.LOCK_SCREEN) {
val screenPreviewClickView: ScreenPreviewClickView =
view.requireViewById(R.id.screen_preview_click_view)
@@ -146,12 +155,6 @@ class PreviewWithClockCarouselSectionController(
guidelineEnd.layoutParams = layoutParams
}
- // TODO (b/270716937) We should handle the single clock case in the clock carousel
- // itself
- val singleClockViewStub: ViewStub = view.requireViewById(R.id.single_clock_view_stub)
- singleClockViewStub.layoutResource = R.layout.single_clock_view
- val singleClockView = singleClockViewStub.inflate() as ViewGroup
-
/**
* Only bind after [Carousel.onAttachedToWindow]. This is to avoid the race condition
* that the flow emits before attached to window where [Carousel.mMotionLayout] is still
@@ -167,7 +170,6 @@ class PreviewWithClockCarouselSectionController(
ClockCarouselViewBinder.bind(
context = context,
carouselView = carouselView,
- singleClockView = singleClockView,
screenPreviewClickView = screenPreviewClickView,
viewModel = viewModel,
clockViewFactory = clockViewFactory,
diff --git a/src/com/android/customization/picker/preview/ui/section/PreviewWithThemeSectionController.kt b/src/com/android/customization/picker/preview/ui/section/PreviewWithThemeSectionController.kt
index 56c6c30a..c4d6be45 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithThemeSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithThemeSectionController.kt
@@ -22,6 +22,7 @@ import android.app.WallpaperManager
import android.content.Context
import androidx.lifecycle.LifecycleOwner
import com.android.customization.model.themedicon.domain.interactor.ThemedIconInteractor
+import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
import com.android.customization.picker.preview.ui.viewmodel.PreviewWithThemeViewModel
import com.android.wallpaper.R
import com.android.wallpaper.model.WallpaperColorsViewModel
@@ -30,6 +31,7 @@ import com.android.wallpaper.module.CurrentWallpaperInfoFactory
import com.android.wallpaper.module.CustomizationSections
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewSectionController
+import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel
import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel
import com.android.wallpaper.util.DisplayUtils
import com.android.wallpaper.util.PreviewUtils
@@ -49,8 +51,10 @@ open class PreviewWithThemeSectionController(
wallpaperPreviewNavigator: WallpaperPreviewNavigator,
private val wallpaperInteractor: WallpaperInteractor,
private val themedIconInteractor: ThemedIconInteractor,
+ private val colorPickerInteractor: ColorPickerInteractor,
wallpaperManager: WallpaperManager,
isTwoPaneAndSmallWidth: Boolean,
+ customizationPickerViewModel: CustomizationPickerViewModel,
) :
ScreenPreviewSectionController(
activity,
@@ -62,7 +66,8 @@ open class PreviewWithThemeSectionController(
wallpaperPreviewNavigator,
wallpaperInteractor,
wallpaperManager,
- isTwoPaneAndSmallWidth
+ isTwoPaneAndSmallWidth,
+ customizationPickerViewModel,
) {
override fun createScreenPreviewViewModel(context: Context): ScreenPreviewViewModel {
return PreviewWithThemeViewModel(
@@ -114,6 +119,7 @@ open class PreviewWithThemeSectionController(
initialExtrasProvider = { getInitialExtras(isOnLockScreen) },
wallpaperInteractor = wallpaperInteractor,
themedIconInteractor = themedIconInteractor,
+ colorPickerInteractor = colorPickerInteractor,
screen = screen,
)
}
diff --git a/src/com/android/customization/picker/preview/ui/viewmodel/PreviewWithThemeViewModel.kt b/src/com/android/customization/picker/preview/ui/viewmodel/PreviewWithThemeViewModel.kt
index 435878dc..83f986da 100644
--- a/src/com/android/customization/picker/preview/ui/viewmodel/PreviewWithThemeViewModel.kt
+++ b/src/com/android/customization/picker/preview/ui/viewmodel/PreviewWithThemeViewModel.kt
@@ -20,12 +20,14 @@ package com.android.customization.picker.preview.ui.viewmodel
import android.app.WallpaperColors
import android.os.Bundle
import com.android.customization.model.themedicon.domain.interactor.ThemedIconInteractor
+import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
import com.android.wallpaper.model.WallpaperInfo
import com.android.wallpaper.module.CustomizationSections
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel
import com.android.wallpaper.util.PreviewUtils
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
/** A ThemePicker version of the [ScreenPreviewViewModel] */
class PreviewWithThemeViewModel(
@@ -35,6 +37,7 @@ class PreviewWithThemeViewModel(
onWallpaperColorChanged: (WallpaperColors?) -> Unit = {},
wallpaperInteractor: WallpaperInteractor,
private val themedIconInteractor: ThemedIconInteractor? = null,
+ colorPickerInteractor: ColorPickerInteractor? = null,
screen: CustomizationSections.Screen,
) :
ScreenPreviewViewModel(
@@ -46,4 +49,16 @@ class PreviewWithThemeViewModel(
screen,
) {
override fun workspaceUpdateEvents(): Flow<Boolean>? = themedIconInteractor?.isActivated
+
+ private val wallpaperIsLoading = super.isLoading
+
+ override val isLoading: Flow<Boolean> =
+ colorPickerInteractor?.let {
+ combine(wallpaperIsLoading, colorPickerInteractor.isApplyingSystemColor) {
+ wallpaperIsLoading,
+ colorIsLoading ->
+ wallpaperIsLoading || colorIsLoading
+ }
+ }
+ ?: wallpaperIsLoading
}
diff --git a/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt b/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
index 6879ffc8..8891b03f 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
@@ -62,6 +62,12 @@ class SlotTabAdapter : RecyclerView.Adapter<SlotTabAdapter.ViewHolder>() {
null
}
)
+ val stateDescription =
+ item.selectedQuickAffordances
+ .find { it.isSelected.value }
+ ?.text
+ ?.asString(holder.itemView.context)
+ stateDescription?.let { holder.itemView.stateDescription = it }
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
diff --git a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
index 68367c8b..091f484e 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
@@ -37,6 +37,7 @@ import com.android.wallpaper.picker.common.icon.ui.viewbinder.IconViewBinder
import com.android.wallpaper.picker.common.icon.ui.viewmodel.Icon
import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collectIndexed
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
@@ -99,13 +100,18 @@ object KeyguardQuickAffordancePickerBinder {
selectedFlags.indexOfFirst { it }
}
}
- .collect { selectedPosition ->
+ .collectIndexed { index, selectedPosition ->
// Scroll the view to show the first selected affordance.
if (selectedPosition != -1) {
// We use "post" because we need to give the adapter item a pass to
// update the view.
affordancesView.post {
- affordancesView.smoothScrollToPosition(selectedPosition)
+ if (index == 0) {
+ // don't animate on initial collection
+ affordancesView.scrollToPosition(selectedPosition)
+ } else {
+ affordancesView.smoothScrollToPosition(selectedPosition)
+ }
}
}
}
diff --git a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordanceSectionViewBinder.kt b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordanceSectionViewBinder.kt
index 28ad51ac..7e1f4d3c 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordanceSectionViewBinder.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordanceSectionViewBinder.kt
@@ -48,7 +48,7 @@ object KeyguardQuickAffordanceSectionViewBinder {
lifecycleOwner.lifecycleScope.launch {
viewModel.summary
- .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.RESUMED)
+ .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.collectLatest { summary ->
TextViewBinder.bind(
view = descriptionView,
diff --git a/src/com/android/customization/picker/quickaffordance/ui/fragment/KeyguardQuickAffordancePickerFragment.kt b/src/com/android/customization/picker/quickaffordance/ui/fragment/KeyguardQuickAffordancePickerFragment.kt
index d5f0d33d..467e5a07 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/fragment/KeyguardQuickAffordancePickerFragment.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/fragment/KeyguardQuickAffordancePickerFragment.kt
@@ -21,8 +21,12 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.get
+import androidx.transition.Transition
+import androidx.transition.doOnStart
import com.android.customization.module.ThemePickerInjector
import com.android.customization.picker.quickaffordance.ui.binder.KeyguardQuickAffordancePickerBinder
import com.android.customization.picker.quickaffordance.ui.binder.KeyguardQuickAffordancePreviewBinder
@@ -30,9 +34,7 @@ import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQui
import com.android.wallpaper.R
import com.android.wallpaper.module.InjectorProvider
import com.android.wallpaper.picker.AppbarFragment
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-@OptIn(ExperimentalCoroutinesApi::class)
class KeyguardQuickAffordancePickerFragment : AppbarFragment() {
companion object {
const val DESTINATION_ID = "quick_affordances"
@@ -77,6 +79,12 @@ class KeyguardQuickAffordancePickerFragment : AppbarFragment() {
viewModel = viewModel,
lifecycleOwner = this,
)
+ postponeEnterTransition()
+ view.post { startPostponedEnterTransition() }
+ (returnTransition as? Transition)?.doOnStart {
+ // Hide preview during exit transition animation
+ view?.findViewById<View>(R.id.preview)?.isVisible = false
+ }
return view
}
@@ -87,4 +95,8 @@ class KeyguardQuickAffordancePickerFragment : AppbarFragment() {
override fun getToolbarColorId(): Int {
return android.R.color.transparent
}
+
+ override fun getToolbarTextColor(): Int {
+ return ContextCompat.getColor(requireContext(), R.color.system_on_surface)
+ }
}
diff --git a/src/com/android/customization/widget/GridTileDrawable.java b/src/com/android/customization/widget/GridTileDrawable.java
index 83cd0b57..b9d2036f 100644
--- a/src/com/android/customization/widget/GridTileDrawable.java
+++ b/src/com/android/customization/widget/GridTileDrawable.java
@@ -55,8 +55,8 @@ public class GridTileDrawable extends Drawable {
for (int r = 0; r < mRows; r++) {
for (int c = 0; c < mCols; c++) {
int saveCount = canvas.save();
- float x = (float) ((r * size / mRows) + SPACE_BETWEEN_ICONS);
- float y = (float) ((c * size / mCols) + SPACE_BETWEEN_ICONS);
+ float y = (float) ((r * size / mRows) + SPACE_BETWEEN_ICONS);
+ float x = (float) ((c * size / mCols) + SPACE_BETWEEN_ICONS);
canvas.translate(x, y);
canvas.drawPath(mTransformedPath, mPaint);
canvas.restoreToCount(saveCount);