summaryrefslogtreecommitdiff
path: root/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/customization/model/color/ColorBundle.java306
-rw-r--r--src/com/android/customization/model/color/ColorBundlePreviewExtractor.java77
-rw-r--r--src/com/android/customization/model/color/ColorCustomizationManager.java56
-rw-r--r--src/com/android/customization/model/color/ColorOption.java34
-rw-r--r--src/com/android/customization/model/color/ColorOptionImpl.kt12
-rw-r--r--src/com/android/customization/model/color/ColorOptionsProvider.java5
-rw-r--r--src/com/android/customization/model/color/ColorProvider.kt312
-rw-r--r--src/com/android/customization/model/color/ColorSeedOption.java252
-rw-r--r--src/com/android/customization/model/color/ThemedWallpaperColorResources.kt72
-rw-r--r--src/com/android/customization/model/grid/GridOption.java20
-rw-r--r--src/com/android/customization/model/grid/GridOptionViewModel.java53
-rw-r--r--src/com/android/customization/model/grid/GridOptionsManager.java2
-rw-r--r--src/com/android/customization/model/mode/DarkModeSectionController.java7
-rw-r--r--src/com/android/customization/model/theme/DefaultThemeProvider.java424
-rw-r--r--src/com/android/customization/model/theme/OverlayThemeExtractor.java293
-rw-r--r--src/com/android/customization/model/theme/ThemeBundle.java432
-rw-r--r--src/com/android/customization/model/theme/ThemeBundleProvider.java51
-rw-r--r--src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java190
-rw-r--r--src/com/android/customization/model/theme/ThemeManager.java149
-rw-r--r--src/com/android/customization/model/theme/custom/ColorOptionsProvider.java171
-rw-r--r--src/com/android/customization/model/theme/custom/CustomTheme.java106
-rw-r--r--src/com/android/customization/model/theme/custom/CustomThemeManager.java116
-rw-r--r--src/com/android/customization/model/theme/custom/FontOptionsProvider.java86
-rw-r--r--src/com/android/customization/model/theme/custom/IconOptionsProvider.java153
-rw-r--r--src/com/android/customization/model/theme/custom/ShapeOptionsProvider.java154
-rw-r--r--src/com/android/customization/model/theme/custom/ThemeComponentOption.java556
-rw-r--r--src/com/android/customization/model/theme/custom/ThemeComponentOptionProvider.java78
-rw-r--r--src/com/android/customization/model/themedicon/ThemedIconSectionController.java13
-rw-r--r--src/com/android/customization/module/CustomizationInjector.kt31
-rw-r--r--src/com/android/customization/module/CustomizationPreferences.java37
-rw-r--r--src/com/android/customization/module/CustomizationPreferences.kt38
-rw-r--r--src/com/android/customization/module/DefaultCustomizationPreferences.java59
-rw-r--r--src/com/android/customization/module/DefaultCustomizationPreferences.kt56
-rw-r--r--src/com/android/customization/module/DefaultCustomizationSections.java32
-rw-r--r--src/com/android/customization/module/StatsLogUserEventLogger.java258
-rw-r--r--src/com/android/customization/module/ThemePickerInjector.kt163
-rw-r--r--src/com/android/customization/module/ThemesUserEventLogger.java44
-rw-r--r--src/com/android/customization/module/logging/AppSessionId.kt43
-rw-r--r--src/com/android/customization/module/logging/SysUiStatsLogger.kt (renamed from src/com/android/customization/module/SysUiStatsLogger.kt)76
-rw-r--r--src/com/android/customization/module/logging/ThemesUserEventLogger.kt64
-rw-r--r--src/com/android/customization/module/logging/ThemesUserEventLoggerImpl.kt299
-rw-r--r--src/com/android/customization/picker/WallpaperPreviewer.java2
-rw-r--r--src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt3
-rw-r--r--src/com/android/customization/picker/clock/shared/ClockSize.kt11
-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.kt10
-rw-r--r--src/com/android/customization/picker/clock/ui/binder/ClockSectionViewBinder.kt53
-rw-r--r--src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt81
-rw-r--r--src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt20
-rw-r--r--src/com/android/customization/picker/clock/ui/section/ClockSectionController.kt62
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt4
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockSizeRadioButtonGroup.kt50
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt212
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockViewFactoryImpl.kt240
-rw-r--r--src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt22
-rw-r--r--src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt26
-rw-r--r--src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt51
-rw-r--r--src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt42
-rw-r--r--src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt26
-rw-r--r--src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt22
-rw-r--r--src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt12
-rw-r--r--src/com/android/customization/picker/color/data/repository/FakeColorPickerRepository.kt81
-rw-r--r--src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt2
-rw-r--r--src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt81
-rw-r--r--src/com/android/customization/picker/color/ui/section/ColorSectionController.kt (renamed from src/com/android/customization/picker/color/ui/section/ColorSectionController2.kt)14
-rw-r--r--src/com/android/customization/picker/color/ui/view/ColorSectionView.kt (renamed from src/com/android/customization/picker/color/ui/view/ColorSectionView2.kt)2
-rw-r--r--src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt12
-rw-r--r--src/com/android/customization/picker/grid/GridFragment.java299
-rw-r--r--src/com/android/customization/picker/grid/GridOptionPreviewer.java105
-rw-r--r--src/com/android/customization/picker/grid/data/repository/GridRepository.kt (renamed from src/com/android/customization/model/grid/data/repository/GridRepository.kt)6
-rw-r--r--src/com/android/customization/picker/grid/domain/interactor/GridInteractor.kt (renamed from src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt)8
-rw-r--r--src/com/android/customization/picker/grid/domain/interactor/GridSnapshotRestorer.kt (renamed from src/com/android/customization/model/grid/domain/interactor/GridSnapshotRestorer.kt)4
-rw-r--r--src/com/android/customization/picker/grid/shared/model/GridOptionItemModel.kt (renamed from src/com/android/customization/model/grid/shared/model/GridOptionItemModel.kt)2
-rw-r--r--src/com/android/customization/picker/grid/shared/model/GridOptionItemsModel.kt (renamed from src/com/android/customization/model/grid/shared/model/GridOptionItemsModel.kt)2
-rw-r--r--src/com/android/customization/picker/grid/ui/binder/GridIconViewBinder.kt (renamed from src/com/android/customization/model/grid/ui/binder/GridIconViewBinder.kt)4
-rw-r--r--src/com/android/customization/picker/grid/ui/binder/GridScreenBinder.kt (renamed from src/com/android/customization/model/grid/ui/binder/GridScreenBinder.kt)8
-rw-r--r--src/com/android/customization/picker/grid/ui/fragment/GridFragment.kt (renamed from src/com/android/customization/model/grid/ui/fragment/GridFragment2.kt)20
-rw-r--r--src/com/android/customization/picker/grid/ui/section/GridSectionController.java (renamed from src/com/android/customization/model/grid/GridSectionController.java)28
-rw-r--r--src/com/android/customization/picker/grid/ui/view/GridSectionView.java (renamed from src/com/android/customization/picker/grid/GridSectionView.java)2
-rw-r--r--src/com/android/customization/picker/grid/ui/viewmodel/GridIconViewModel.kt (renamed from src/com/android/customization/model/grid/ui/viewmodel/GridIconViewModel.kt)2
-rw-r--r--src/com/android/customization/picker/grid/ui/viewmodel/GridScreenViewModel.kt (renamed from src/com/android/customization/model/grid/ui/viewmodel/GridScreenViewModel.kt)6
-rw-r--r--src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModel.kt11
-rw-r--r--src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt6
-rw-r--r--src/com/android/customization/picker/preview/ui/section/PreviewWithThemeSectionController.kt40
-rw-r--r--src/com/android/customization/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt38
-rw-r--r--src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt12
-rw-r--r--src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordanceSnapshotRestorer.kt9
-rw-r--r--src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt4
-rw-r--r--src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt24
-rw-r--r--src/com/android/customization/picker/quickaffordance/ui/section/KeyguardQuickAffordanceSectionController.kt8
-rw-r--r--src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt39
-rw-r--r--src/com/android/customization/picker/theme/CustomThemeActivity.java421
-rw-r--r--src/com/android/customization/picker/theme/CustomThemeComponentFragment.java121
-rw-r--r--src/com/android/customization/picker/theme/CustomThemeNameFragment.java132
-rw-r--r--src/com/android/customization/picker/theme/CustomThemeStepFragment.java116
-rw-r--r--src/com/android/customization/picker/theme/ThemeFragment.java402
-rw-r--r--src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java165
-rw-r--r--src/com/android/customization/picker/theme/ThemeInfoView.java84
-rw-r--r--src/com/android/customization/picker/theme/ThemeOptionPreviewer.java405
-rw-r--r--src/com/android/customization/picker/themedicon/ThemedIconSectionView.java14
-rw-r--r--src/com/android/customization/widget/OptionSelectorController.java469
101 files changed, 1643 insertions, 7895 deletions
diff --git a/src/com/android/customization/model/color/ColorBundle.java b/src/com/android/customization/model/color/ColorBundle.java
deleted file mode 100644
index d34f3fc0..00000000
--- a/src/com/android/customization/model/color/ColorBundle.java
+++ /dev/null
@@ -1,306 +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.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.Dimension;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.customization.model.ResourceConstants;
-import com.android.systemui.monet.Style;
-import com.android.wallpaper.R;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Represents a preset color available for the user to chose as their theming option.
- */
-public class ColorBundle extends ColorOption {
-
- private final PreviewInfo mPreviewInfo;
-
- @VisibleForTesting ColorBundle(String title,
- Map<String, String> overlayPackages, boolean isDefault, Style style, int index,
- PreviewInfo previewInfo) {
- super(title, overlayPackages, isDefault, style, index);
- mPreviewInfo = previewInfo;
- }
-
- @Override
- public void bindThumbnailTile(View view) {
- Resources res = view.getContext().getResources();
- int primaryColor = mPreviewInfo.resolvePrimaryColor(res);
- int secondaryColor = mPreviewInfo.resolveSecondaryColor(res);
-
- for (int i = 0; i < mPreviewColorIds.length; i++) {
- ImageView colorPreviewImageView = view.findViewById(mPreviewColorIds[i]);
- int color = i % 2 == 0 ? primaryColor : secondaryColor;
- colorPreviewImageView.getDrawable().setColorFilter(color, PorterDuff.Mode.SRC);
- }
- view.setContentDescription(getContentDescription(view.getContext()));
- }
-
- @Override
- public PreviewInfo getPreviewInfo() {
- return mPreviewInfo;
- }
-
- @Override
- public int getLayoutResId() {
- return R.layout.color_option;
- }
-
- @Override
- public String getSource() {
- return ColorOptionsProvider.COLOR_SOURCE_PRESET;
- }
-
- /**
- * The preview information of {@link ColorBundle}
- */
- public static class PreviewInfo implements ColorOption.PreviewInfo {
- @ColorInt
- public final int secondaryColorLight;
- @ColorInt public final int secondaryColorDark;
- // Monet system palette and accent colors
- @ColorInt public final int primaryColorLight;
- @ColorInt public final int primaryColorDark;
- @Dimension
- public final int bottomSheetCornerRadius;
-
- @ColorInt private int mOverrideSecondaryColorLight = Color.TRANSPARENT;
- @ColorInt private int mOverrideSecondaryColorDark = Color.TRANSPARENT;
- @ColorInt private int mOverridePrimaryColorLight = Color.TRANSPARENT;
- @ColorInt private int mOverridePrimaryColorDark = Color.TRANSPARENT;
-
- private PreviewInfo(
- int secondaryColorLight, int secondaryColorDark, int colorSystemPaletteLight,
- int primaryColorDark, @Dimension int cornerRadius) {
- this.secondaryColorLight = secondaryColorLight;
- this.secondaryColorDark = secondaryColorDark;
- this.primaryColorLight = colorSystemPaletteLight;
- this.primaryColorDark = primaryColorDark;
- this.bottomSheetCornerRadius = cornerRadius;
- }
-
- /**
- * Returns the accent color to be applied corresponding with the current configuration's
- * UI mode.
- * @return one of {@link #secondaryColorDark} or {@link #secondaryColorLight}
- */
- @ColorInt
- public int resolveSecondaryColor(Resources res) {
- boolean night = (res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
- if (mOverrideSecondaryColorDark != Color.TRANSPARENT
- || mOverrideSecondaryColorLight != Color.TRANSPARENT) {
- return night ? mOverrideSecondaryColorDark : mOverrideSecondaryColorLight;
- }
- return night ? secondaryColorDark : secondaryColorLight;
- }
-
- /**
- * Returns the palette (main) color to be applied corresponding with the current
- * configuration's UI mode.
- * @return one of {@link #secondaryColorDark} or {@link #secondaryColorLight}
- */
- @ColorInt
- public int resolvePrimaryColor(Resources res) {
- boolean night = (res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
- if (mOverridePrimaryColorDark != Color.TRANSPARENT
- || mOverridePrimaryColorLight != Color.TRANSPARENT) {
- return night ? mOverridePrimaryColorDark : mOverridePrimaryColorLight;
- }
- return night ? primaryColorDark
- : primaryColorLight;
- }
-
- /**
- * Sets accent colors to override the ones in this bundle
- */
- public void setOverrideAccentColors(int overrideColorAccentLight,
- int overrideColorAccentDark) {
- mOverrideSecondaryColorLight = overrideColorAccentLight;
- mOverrideSecondaryColorDark = overrideColorAccentDark;
- }
-
- /**
- * Sets palette colors to override the ones in this bundle
- */
- public void setOverridePaletteColors(int overrideColorPaletteLight,
- int overrideColorPaletteDark) {
- mOverridePrimaryColorLight = overrideColorPaletteLight;
- mOverridePrimaryColorDark = overrideColorPaletteDark;
- }
- }
-
- /**
- * The builder of ColorBundle
- */
- public static class Builder {
- protected String mTitle;
- @ColorInt private int mSecondaryColorLight = Color.TRANSPARENT;
- @ColorInt private int mSecondaryColorDark = Color.TRANSPARENT;
- // System and Monet colors
- @ColorInt private int mPrimaryColorLight = Color.TRANSPARENT;
- @ColorInt private int mPrimaryColorDark = Color.TRANSPARENT;
- private boolean mIsDefault;
- private Style mStyle = Style.TONAL_SPOT;
- private int mIndex;
- protected Map<String, String> mPackages = new HashMap<>();
-
- /**
- * Builds the ColorBundle
- * @param context {@link Context}
- * @return new {@link ColorBundle} object
- */
- public ColorBundle build(Context context) {
- if (mTitle == null) {
- mTitle = context.getString(R.string.adaptive_color_title);
- }
- return new ColorBundle(mTitle, mPackages, mIsDefault, mStyle, mIndex,
- createPreviewInfo(context));
- }
-
- /**
- * Creates preview information
- * @param context the {@link Context}
- * @return the {@link PreviewInfo} object
- */
- public PreviewInfo createPreviewInfo(@NonNull Context context) {
- Resources system = context.getResources().getSystem();
- return new PreviewInfo(mSecondaryColorLight,
- mSecondaryColorDark, mPrimaryColorLight, mPrimaryColorDark,
- system.getDimensionPixelOffset(
- system.getIdentifier(ResourceConstants.CONFIG_CORNERRADIUS, "dimen",
- ResourceConstants.ANDROID_PACKAGE)));
- }
-
- public Map<String, String> getPackages() {
- return Collections.unmodifiableMap(mPackages);
- }
-
- /**
- * Gets title of this {@link ColorBundle} object
- * @return title string
- */
- public String getTitle() {
- return mTitle;
- }
-
- /**
- * Sets title of bundle
- * @param title specified title
- * @return this of {@link Builder}
- */
- public Builder setTitle(String title) {
- mTitle = title;
- return this;
- }
-
- /**
- * Sets color accent (light)
- * @param colorSecondaryLight color accent light in {@link ColorInt}
- * @return this of {@link Builder}
- */
- public Builder setColorSecondaryLight(@ColorInt int colorSecondaryLight) {
- mSecondaryColorLight = colorSecondaryLight;
- return this;
- }
-
- /**
- * Sets color accent (dark)
- * @param colorSecondaryDark color accent dark in {@link ColorInt}
- * @return this of {@link Builder}
- */
- public Builder setColorSecondaryDark(@ColorInt int colorSecondaryDark) {
- mSecondaryColorDark = colorSecondaryDark;
- return this;
- }
-
- /**
- * Sets color system palette (light)
- * @param colorPrimaryLight color system palette in {@link ColorInt}
- * @return this of {@link Builder}
- */
- public Builder setColorPrimaryLight(@ColorInt int colorPrimaryLight) {
- mPrimaryColorLight = colorPrimaryLight;
- return this;
- }
-
- /**
- * Sets color system palette (dark)
- * @param colorPrimaryDark color system palette in {@link ColorInt}
- * @return this of {@link Builder}
- */
- public Builder setColorPrimaryDark(@ColorInt int colorPrimaryDark) {
- mPrimaryColorDark = colorPrimaryDark;
- return this;
- }
-
- /**
- * Sets overlay package for bundle
- * @param category the category of bundle
- * @param packageName tha name of package in the category
- * @return this of {@link Builder}
- */
- public Builder addOverlayPackage(String category, String packageName) {
- mPackages.put(category, packageName);
- return this;
- }
-
- /**
- * Sets the style of this color seed
- * @param style color style of {@link Style}
- * @return this of {@link Builder}
- */
- public Builder setStyle(Style style) {
- mStyle = style;
- return this;
- }
-
- /**
- * Sets color option index of bundle
- * @param index color option index
- * @return this of {@link Builder}
- */
- public Builder setIndex(int index) {
- mIndex = index;
- return this;
- }
-
- /**
- * Sets as default bundle
- * @return this of {@link Builder}
- */
- public Builder asDefault() {
- mIsDefault = true;
- return this;
- }
- }
-}
diff --git a/src/com/android/customization/model/color/ColorBundlePreviewExtractor.java b/src/com/android/customization/model/color/ColorBundlePreviewExtractor.java
deleted file mode 100644
index 55b637f6..00000000
--- a/src/com/android/customization/model/color/ColorBundlePreviewExtractor.java
+++ /dev/null
@@ -1,77 +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 com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SYSTEM_PALETTE;
-import static com.android.customization.model.color.ColorUtils.toColorString;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.ColorInt;
-
-import com.android.systemui.monet.ColorScheme;
-import com.android.systemui.monet.Style;
-
-/**
- * Utility class to read all the details of a color bundle for previewing it
- * (eg, actual color values)
- */
-class ColorBundlePreviewExtractor {
-
- private static final String TAG = "ColorBundlePreviewExtractor";
-
- private final PackageManager mPackageManager;
-
- ColorBundlePreviewExtractor(Context context) {
- mPackageManager = context.getPackageManager();
- }
-
- void addSecondaryColor(ColorBundle.Builder builder, @ColorInt int color) {
- ColorScheme darkColorScheme = new ColorScheme(color, true);
- ColorScheme lightColorScheme = new ColorScheme(color, false);
- int lightSecondary = lightColorScheme.getAccentColor();
- int darkSecondary = darkColorScheme.getAccentColor();
- builder.addOverlayPackage(OVERLAY_CATEGORY_COLOR, toColorString(color))
- .setColorSecondaryLight(lightSecondary)
- .setColorSecondaryDark(darkSecondary);
- }
-
- void addPrimaryColor(ColorBundle.Builder builder, @ColorInt int color) {
- ColorScheme darkColorScheme = new ColorScheme(color, true);
- ColorScheme lightColorScheme = new ColorScheme(color, false);
- int lightPrimary = lightColorScheme.getAccentColor();
- int darkPrimary = darkColorScheme.getAccentColor();
- builder.addOverlayPackage(OVERLAY_CATEGORY_SYSTEM_PALETTE, toColorString(color))
- .setColorPrimaryLight(lightPrimary)
- .setColorPrimaryDark(darkPrimary);
- }
-
- void addColorStyle(ColorBundle.Builder builder, String styleName) {
- Style s = Style.TONAL_SPOT;
- if (!TextUtils.isEmpty(styleName)) {
- try {
- s = Style.valueOf(styleName);
- } catch (IllegalArgumentException e) {
- Log.i(TAG, "Unknown style : " + styleName + ". Will default to TONAL_SPOT.");
- }
- }
- builder.setStyle(s);
- }
-}
diff --git a/src/com/android/customization/model/color/ColorCustomizationManager.java b/src/com/android/customization/model/color/ColorCustomizationManager.java
index 790c86f2..a09efd26 100644
--- a/src/com/android/customization/model/color/ColorCustomizationManager.java
+++ b/src/com/android/customization/model/color/ColorCustomizationManager.java
@@ -15,6 +15,11 @@
*/
package com.android.customization.model.color;
+import static android.stats.style.StyleEnums.COLOR_SOURCE_HOME_SCREEN_WALLPAPER;
+import static android.stats.style.StyleEnums.COLOR_SOURCE_LOCK_SCREEN_WALLPAPER;
+import static android.stats.style.StyleEnums.COLOR_SOURCE_PRESET_COLOR;
+import static android.stats.style.StyleEnums.COLOR_SOURCE_UNSPECIFIED;
+
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SYSTEM_PALETTE;
import static com.android.customization.model.color.ColorOptionsProvider.COLOR_SOURCE_PRESET;
@@ -27,6 +32,7 @@ import android.app.WallpaperColors;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.graphics.Color;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -41,6 +47,7 @@ import com.android.customization.model.CustomizationManager;
import com.android.customization.model.ResourceConstants;
import com.android.customization.model.color.ColorOptionsProvider.ColorSource;
import com.android.customization.model.theme.OverlayManagerCompat;
+import com.android.customization.module.logging.ThemesUserEventLogger;
import com.android.wallpaper.R;
import org.json.JSONArray;
@@ -182,22 +189,7 @@ public class ColorCustomizationManager implements CustomizationManager<ColorOpti
lockWallpaperColors = null;
}
mProvider.fetch(callback, reload, mHomeWallpaperColors,
- lockWallpaperColors, /* shouldUseRevampedUi= */ false);
- }
-
- /**
- * Fetch options function for the customization hub revamped UI
- *
- * TODO (b/276417460): refactor to reduce code repetition with the other fetch options function
- */
- public void fetchRevampedUIOptions(OptionsFetchedListener<ColorOption> callback,
- boolean reload) {
- WallpaperColors lockWallpaperColors = mLockWallpaperColors;
- if (lockWallpaperColors != null && mLockWallpaperColors.equals(mHomeWallpaperColors)) {
- lockWallpaperColors = null;
- }
- mProvider.fetch(callback, reload, mHomeWallpaperColors,
- lockWallpaperColors, /* shouldUseRevampedUi= */ true);
+ lockWallpaperColors);
}
/**
@@ -220,6 +212,38 @@ public class ColorCustomizationManager implements CustomizationManager<ColorOpti
return mCurrentOverlays;
}
+ /** */
+ public int getCurrentColorSourceForLogging() {
+ String colorSource = getCurrentColorSource();
+ if (colorSource == null) {
+ return COLOR_SOURCE_UNSPECIFIED;
+ }
+ return switch (colorSource) {
+ case ColorOptionsProvider.COLOR_SOURCE_PRESET -> COLOR_SOURCE_PRESET_COLOR;
+ case ColorOptionsProvider.COLOR_SOURCE_HOME -> COLOR_SOURCE_HOME_SCREEN_WALLPAPER;
+ case ColorOptionsProvider.COLOR_SOURCE_LOCK -> COLOR_SOURCE_LOCK_SCREEN_WALLPAPER;
+ default -> COLOR_SOURCE_UNSPECIFIED;
+ };
+ }
+
+ /** */
+ public int getCurrentStyleForLogging() {
+ String style = getCurrentStyle();
+ return style != null ? style.hashCode() : 0;
+ }
+
+ /** */
+ public int getCurrentSeedColorForLogging() {
+ String seedColor = getCurrentOverlays().get(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ if (seedColor == null || seedColor.isEmpty()) {
+ return ThemesUserEventLogger.NULL_SEED_COLOR;
+ }
+ if (!seedColor.startsWith("#")) {
+ seedColor = "#" + seedColor;
+ }
+ return Color.parseColor(seedColor);
+ }
+
/**
* @return The source of the currently applied color. One of
* {@link ColorOptionsProvider#COLOR_SOURCE_HOME},{@link ColorOptionsProvider#COLOR_SOURCE_LOCK}
diff --git a/src/com/android/customization/model/color/ColorOption.java b/src/com/android/customization/model/color/ColorOption.java
index 216bb9ba..f57aa860 100644
--- a/src/com/android/customization/model/color/ColorOption.java
+++ b/src/com/android/customization/model/color/ColorOption.java
@@ -19,6 +19,7 @@ import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SYSTEM_PALETTE;
import android.content.Context;
+import android.graphics.Color;
import android.text.TextUtils;
import android.util.Log;
@@ -27,6 +28,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.customization.model.CustomizationManager;
import com.android.customization.model.CustomizationOption;
import com.android.customization.model.color.ColorOptionsProvider.ColorSource;
+import com.android.customization.module.logging.ThemesUserEventLogger;
import com.android.systemui.monet.Style;
import com.android.wallpaper.R;
@@ -52,8 +54,6 @@ public abstract class ColorOption implements CustomizationOption<ColorOption> {
static final String TIMESTAMP_FIELD = "_applied_timestamp";
protected final Map<String, String> mPackagesByCategory;
- protected final int[] mPreviewColorIds = {R.id.color_preview_0, R.id.color_preview_1,
- R.id.color_preview_2, R.id.color_preview_3};
private final String mTitle;
private final boolean mIsDefault;
private final Style mStyle;
@@ -86,6 +86,9 @@ public abstract class ColorOption implements CustomizationOption<ColorOption> {
if (mIsDefault) {
String serializedOverlays = colorManager.getStoredOverlays();
+ // a default color option is active if the manager has no stored overlays or current
+ // overlays, or the stored overlay does not contain either category system palette or
+ // category color
return (TextUtils.isEmpty(serializedOverlays) || EMPTY_JSON.equals(serializedOverlays)
|| colorManager.getCurrentOverlays().isEmpty() || !(serializedOverlays.contains(
OVERLAY_CATEGORY_SYSTEM_PALETTE) || serializedOverlays.contains(
@@ -100,6 +103,22 @@ public abstract class ColorOption implements CustomizationOption<ColorOption> {
}
/**
+ * Gets the seed color from the overlay packages for logging.
+ *
+ * @return an int representing the seed color, or NULL_SEED_COLOR
+ */
+ public int getSeedColorForLogging() {
+ String seedColor = mPackagesByCategory.get(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ if (seedColor == null || seedColor.isEmpty()) {
+ return ThemesUserEventLogger.NULL_SEED_COLOR;
+ }
+ if (!seedColor.startsWith("#")) {
+ seedColor = "#" + seedColor;
+ }
+ return Color.parseColor(seedColor);
+ }
+
+ /**
* This is similar to #equals() but it only compares this theme's packages with the other, that
* is, it will return true if applying this theme has the same effect of applying the given one.
*/
@@ -208,6 +227,12 @@ public abstract class ColorOption implements CustomizationOption<ColorOption> {
public abstract String getSource();
/**
+ * @return the source of this color option for logging
+ */
+ @ThemesUserEventLogger.ColorSource
+ public abstract int getSourceForLogging();
+
+ /**
* @return the style of this color option
*/
public Style getStyle() {
@@ -215,6 +240,11 @@ public abstract class ColorOption implements CustomizationOption<ColorOption> {
}
/**
+ * @return the style of this color option for logging
+ */
+ public abstract int getStyleForLogging();
+
+ /**
* @return the index of this color option
*/
public int getIndex() {
diff --git a/src/com/android/customization/model/color/ColorOptionImpl.kt b/src/com/android/customization/model/color/ColorOptionImpl.kt
index 3273ce26..f0905283 100644
--- a/src/com/android/customization/model/color/ColorOptionImpl.kt
+++ b/src/com/android/customization/model/color/ColorOptionImpl.kt
@@ -17,6 +17,7 @@
package com.android.customization.model.color
import android.content.Context
+import android.stats.style.StyleEnums
import android.view.View
import androidx.annotation.ColorInt
import com.android.customization.model.color.ColorOptionsProvider.ColorSource
@@ -68,6 +69,17 @@ class ColorOptionImpl(
return source
}
+ override fun getSourceForLogging(): Int {
+ return when (getSource()) {
+ ColorOptionsProvider.COLOR_SOURCE_PRESET -> StyleEnums.COLOR_SOURCE_PRESET_COLOR
+ ColorOptionsProvider.COLOR_SOURCE_HOME -> StyleEnums.COLOR_SOURCE_HOME_SCREEN_WALLPAPER
+ ColorOptionsProvider.COLOR_SOURCE_LOCK -> StyleEnums.COLOR_SOURCE_LOCK_SCREEN_WALLPAPER
+ else -> StyleEnums.COLOR_SOURCE_UNSPECIFIED
+ }
+ }
+
+ override fun getStyleForLogging(): Int = style.toString().hashCode()
+
class Builder {
var title: String? = null
diff --git a/src/com/android/customization/model/color/ColorOptionsProvider.java b/src/com/android/customization/model/color/ColorOptionsProvider.java
index 166b4da7..9703907d 100644
--- a/src/com/android/customization/model/color/ColorOptionsProvider.java
+++ b/src/com/android/customization/model/color/ColorOptionsProvider.java
@@ -70,12 +70,9 @@ public interface ColorOptionsProvider {
* @param homeWallpaperColors to get seed colors from
* @param lockWallpaperColors WallpaperColors from the lockscreen wallpaper to get seeds from,
* if different than homeWallpaperColors
- * @param shouldUseRevampedUi fetches color options with new preview mappings for the revamped
- * UI if set to true
*/
void fetch(OptionsFetchedListener<ColorOption> callback, boolean reload,
@Nullable WallpaperColors homeWallpaperColors,
- @Nullable WallpaperColors lockWallpaperColors,
- boolean shouldUseRevampedUi
+ @Nullable WallpaperColors lockWallpaperColors
);
}
diff --git a/src/com/android/customization/model/color/ColorProvider.kt b/src/com/android/customization/model/color/ColorProvider.kt
index 74a4051b..6fdfd2ca 100644
--- a/src/com/android/customization/model/color/ColorProvider.kt
+++ b/src/com/android/customization/model/color/ColorProvider.kt
@@ -48,6 +48,7 @@ import kotlinx.coroutines.withContext
/**
* Default implementation of {@link ColorOptionsProvider} that reads preset colors from a stub APK.
+ * TODO (b/311212666): Make [ColorProvider] and [ColorCustomizationManager] injectable
*/
class ColorProvider(private val context: Context, stubPackageName: String) :
ResourcesApkProvider(context, stubPackageName), ColorOptionsProvider {
@@ -68,8 +69,6 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
arrayOf(Style.TONAL_SPOT, Style.SPRITZ, Style.VIBRANT, Style.EXPRESSIVE)
else arrayOf(Style.TONAL_SPOT)
- private val monochromeEnabled =
- InjectorProvider.getInjector().getFlags().isMonochromaticThemeEnabled(mContext)
private var monochromeBundleName: String? = null
private val scope =
@@ -93,7 +92,6 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
reload: Boolean,
homeWallpaperColors: WallpaperColors?,
lockWallpaperColors: WallpaperColors?,
- shouldUseRevampedUi: Boolean
) {
val wallpaperColorsChanged =
this.homeWallpaperColors != homeWallpaperColors ||
@@ -106,13 +104,12 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
scope.launch {
try {
if (colorBundles == null || reload) {
- loadPreset(shouldUseRevampedUi)
+ loadPreset()
}
if (wallpaperColorsChanged || reload) {
loadSeedColors(
homeWallpaperColors,
lockWallpaperColors,
- shouldUseRevampedUi
)
}
} catch (e: Throwable) {
@@ -138,7 +135,6 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
private fun loadSeedColors(
homeWallpaperColors: WallpaperColors?,
lockWallpaperColors: WallpaperColors?,
- shouldUseRevampedUi: Boolean,
) {
if (homeWallpaperColors == null) return
@@ -159,7 +155,6 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
if (shouldLockColorsGoFirst) COLOR_SOURCE_LOCK else COLOR_SOURCE_HOME,
true,
bundles,
- shouldUseRevampedUi
)
// Second half of the colors
buildColorSeeds(
@@ -168,7 +163,6 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
if (shouldLockColorsGoFirst) COLOR_SOURCE_HOME else COLOR_SOURCE_LOCK,
false,
bundles,
- shouldUseRevampedUi
)
} else {
buildColorSeeds(
@@ -177,35 +171,20 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
COLOR_SOURCE_HOME,
true,
bundles,
- shouldUseRevampedUi
)
}
- if (shouldUseRevampedUi) {
- // Insert monochrome in the second position if it is enabled and included in preset
- // colors
- if (monochromeEnabled) {
- monochromeBundleName?.let {
- bundles.add(
- 1,
- buildRevampedUIPreset(
- it,
- -1,
- Style.MONOCHROMATIC,
- ColorType.WALLPAPER_COLOR
- )
- )
- }
+ // Insert monochrome in the second position if it is enabled and included in preset
+ // colors
+ if (InjectorProvider.getInjector().getFlags().isMonochromaticThemeEnabled(mContext)) {
+ monochromeBundleName?.let {
+ bundles.add(1, buildPreset(it, -1, Style.MONOCHROMATIC, ColorType.WALLPAPER_COLOR))
}
- bundles.addAll(
- colorBundles?.filterNot {
- (it as ColorOptionImpl).type == ColorType.WALLPAPER_COLOR
- }
- ?: emptyList()
- )
- } else {
- bundles.addAll(colorBundles?.filterNot { it is ColorSeedOption } ?: emptyList())
}
+ bundles.addAll(
+ colorBundles?.filterNot { (it as ColorOptionImpl).type == ColorType.WALLPAPER_COLOR }
+ ?: emptyList()
+ )
colorBundles = bundles
}
@@ -215,13 +194,12 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
source: String,
containsDefault: Boolean,
bundles: MutableList<ColorOption>,
- shouldUseRevampedUi: Boolean,
) {
val seedColors = ColorScheme.getSeedColors(wallpaperColors)
val defaultSeed = seedColors.first()
- buildBundle(defaultSeed, 0, containsDefault, source, bundles, shouldUseRevampedUi)
+ buildBundle(defaultSeed, 0, containsDefault, source, bundles)
for ((i, colorInt) in seedColors.drop(1).take(maxColors - 1).withIndex()) {
- buildBundle(colorInt, i + 1, false, source, bundles, shouldUseRevampedUi)
+ buildBundle(colorInt, i + 1, false, source, bundles)
}
}
@@ -231,102 +209,41 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
isDefault: Boolean,
source: String,
bundles: MutableList<ColorOption>,
- shouldUseRevampedUi: Boolean,
) {
// TODO(b/202145216): Measure time cost in the loop.
- if (shouldUseRevampedUi) {
- for (style in styleList) {
- val lightColorScheme = ColorScheme(colorInt, /* darkTheme= */ false, style)
- val darkColorScheme = ColorScheme(colorInt, /* darkTheme= */ true, style)
- val builder = ColorOptionImpl.Builder()
- builder.lightColors = getRevampedUILightColorPreview(lightColorScheme)
- builder.darkColors = getRevampedUIDarkColorPreview(darkColorScheme)
- builder.addOverlayPackage(
- OVERLAY_CATEGORY_SYSTEM_PALETTE,
- if (isDefault) "" else toColorString(colorInt)
- )
- builder.title =
- when (style) {
- Style.TONAL_SPOT ->
- context.getString(R.string.content_description_dynamic_color_option)
- Style.SPRITZ ->
- context.getString(R.string.content_description_neutral_color_option)
- Style.VIBRANT ->
- context.getString(R.string.content_description_vibrant_color_option)
- Style.EXPRESSIVE ->
- context.getString(R.string.content_description_expressive_color_option)
- else -> context.getString(R.string.content_description_dynamic_color_option)
- }
- builder.source = source
- builder.style = style
- // Color option index value starts from 1.
- builder.index = i + 1
- builder.isDefault = isDefault
- builder.type = ColorType.WALLPAPER_COLOR
- bundles.add(builder.build())
- }
- } else {
- for (style in styleList) {
- val lightColorScheme = ColorScheme(colorInt, /* darkTheme= */ false, style)
- val darkColorScheme = ColorScheme(colorInt, /* darkTheme= */ true, style)
- val builder = ColorSeedOption.Builder()
- builder
- .setLightColors(lightColorScheme.getLightColorPreview())
- .setDarkColors(darkColorScheme.getDarkColorPreview())
- .addOverlayPackage(
- OVERLAY_CATEGORY_SYSTEM_PALETTE,
- if (isDefault) "" else toColorString(colorInt)
- )
- .addOverlayPackage(
- OVERLAY_CATEGORY_COLOR,
- if (isDefault) "" else toColorString(colorInt)
- )
- .setSource(source)
- .setStyle(style)
- // Color option index value starts from 1.
- .setIndex(i + 1)
-
- if (isDefault) builder.asDefault()
-
- bundles.add(builder.build())
- }
- }
- }
-
- /**
- * Returns the colors for the light theme version of the preview of a ColorScheme based on this
- * order: top left, top right, bottom left, bottom right
- */
- @ColorInt
- private fun ColorScheme.getLightColorPreview(): IntArray {
- return when (this.style) {
- Style.EXPRESSIVE ->
- intArrayOf(
- setAlphaComponent(this.accent1.s100, ALPHA_MASK),
- setAlphaComponent(this.accent1.s100, ALPHA_MASK),
- ColorStateList.valueOf(this.neutral2.s500).withLStar(80f).colors[0],
- setAlphaComponent(this.accent2.s500, ALPHA_MASK)
- )
- else ->
- intArrayOf(
- setAlphaComponent(this.accent1.s100, ALPHA_MASK),
- setAlphaComponent(this.accent1.s100, ALPHA_MASK),
- ColorStateList.valueOf(this.accent3.s500).withLStar(85f).colors[0],
- setAlphaComponent(this.accent1.s500, ALPHA_MASK)
- )
+ for (style in styleList) {
+ val lightColorScheme = ColorScheme(colorInt, /* darkTheme= */ false, style)
+ val darkColorScheme = ColorScheme(colorInt, /* darkTheme= */ true, style)
+ val builder = ColorOptionImpl.Builder()
+ builder.lightColors = getLightColorPreview(lightColorScheme)
+ builder.darkColors = getDarkColorPreview(darkColorScheme)
+ builder.addOverlayPackage(
+ OVERLAY_CATEGORY_SYSTEM_PALETTE,
+ if (isDefault) "" else toColorString(colorInt)
+ )
+ builder.title =
+ when (style) {
+ Style.TONAL_SPOT ->
+ context.getString(R.string.content_description_dynamic_color_option)
+ Style.SPRITZ ->
+ context.getString(R.string.content_description_neutral_color_option)
+ Style.VIBRANT ->
+ context.getString(R.string.content_description_vibrant_color_option)
+ Style.EXPRESSIVE ->
+ context.getString(R.string.content_description_expressive_color_option)
+ else -> context.getString(R.string.content_description_dynamic_color_option)
+ }
+ builder.source = source
+ builder.style = style
+ // Color option index value starts from 1.
+ builder.index = i + 1
+ builder.isDefault = isDefault
+ builder.type = ColorType.WALLPAPER_COLOR
+ bundles.add(builder.build())
}
}
/**
- * Returns the color for the dark theme version of the preview of a ColorScheme based on this
- * order: top left, top right, bottom left, bottom right
- */
- @ColorInt
- private fun ColorScheme.getDarkColorPreview(): IntArray {
- return getLightColorPreview()
- }
-
- /**
* Returns the light theme version of the Revamped UI preview of a ColorScheme based on this
* order: top left, top right, bottom left, bottom right
*
@@ -334,7 +251,7 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
* LStar 85, and Tertiary LStar 70
*/
@ColorInt
- private fun getRevampedUILightColorPreview(colorScheme: ColorScheme): IntArray {
+ private fun getLightColorPreview(colorScheme: ColorScheme): IntArray {
return intArrayOf(
setAlphaComponent(colorScheme.accent1.s600, ALPHA_MASK),
setAlphaComponent(colorScheme.accent1.s600, ALPHA_MASK),
@@ -351,7 +268,7 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
* 35, and Tertiary LStar 70
*/
@ColorInt
- private fun getRevampedUIDarkColorPreview(colorScheme: ColorScheme): IntArray {
+ private fun getDarkColorPreview(colorScheme: ColorScheme): IntArray {
return intArrayOf(
setAlphaComponent(colorScheme.accent1.s200, ALPHA_MASK),
setAlphaComponent(colorScheme.accent1.s200, ALPHA_MASK),
@@ -368,7 +285,7 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
* LStar 85, and Tertiary LStar 70
*/
@ColorInt
- private fun getRevampedUILightMonochromePreview(colorScheme: ColorScheme): IntArray {
+ private fun getLightMonochromePreview(colorScheme: ColorScheme): IntArray {
return intArrayOf(
setAlphaComponent(colorScheme.accent1.s1000, ALPHA_MASK),
setAlphaComponent(colorScheme.accent1.s1000, ALPHA_MASK),
@@ -385,7 +302,7 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
* LStar 35, and Tertiary LStar 70
*/
@ColorInt
- private fun getRevampedUIDarkMonochromePreview(colorScheme: ColorScheme): IntArray {
+ private fun getDarkMonochromePreview(colorScheme: ColorScheme): IntArray {
return intArrayOf(
setAlphaComponent(colorScheme.accent1.s10, ALPHA_MASK),
setAlphaComponent(colorScheme.accent1.s10, ALPHA_MASK),
@@ -398,7 +315,7 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
* Returns the Revamped UI preview of a preset ColorScheme based on this order: top left, top
* right, bottom left, bottom right
*/
- private fun getRevampedUIPresetColorPreview(colorScheme: ColorScheme, seed: Int): IntArray {
+ private fun getPresetColorPreview(colorScheme: ColorScheme, seed: Int): IntArray {
val colors =
when (colorScheme.style) {
Style.FRUIT_SALAD -> intArrayOf(seed, colorScheme.accent1.s200)
@@ -414,22 +331,8 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
)
}
- private fun ColorScheme.getPresetColorPreview(seed: Int): IntArray {
- return when (this.style) {
- Style.FRUIT_SALAD -> intArrayOf(seed, this.accent1.s100)
- Style.TONAL_SPOT -> intArrayOf(this.accentColor, this.accentColor)
- Style.MONOCHROMATIC ->
- intArrayOf(
- setAlphaComponent(0x000000, 255),
- setAlphaComponent(0xFFFFFF, 255),
- )
- else -> intArrayOf(this.accent1.s100, this.accent1.s100)
- }
- }
-
- private suspend fun loadPreset(shouldUseRevampedUi: Boolean) =
+ private suspend fun loadPreset() =
withContext(Dispatchers.IO) {
- val extractor = ColorBundlePreviewExtractor(mContext)
val bundles: MutableList<ColorOption> = ArrayList()
val bundleNames =
@@ -438,95 +341,50 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
var index = 1
val maxPresetColors = if (themeStyleEnabled) bundleNames.size else MAX_PRESET_COLORS
- if (shouldUseRevampedUi) {
- // keep track of whether monochrome is included in preset colors to determine
- // inclusion in wallpaper colors
- var hasMonochrome = false
- for (bundleName in bundleNames.take(maxPresetColors)) {
- if (themeStyleEnabled) {
- val styleName =
- try {
- getItemStringFromStub(COLOR_BUNDLE_STYLE_PREFIX, bundleName)
- } catch (e: Resources.NotFoundException) {
- null
- }
- val style =
- try {
- if (styleName != null) Style.valueOf(styleName)
- else Style.TONAL_SPOT
- } catch (e: IllegalArgumentException) {
- Style.TONAL_SPOT
- }
-
- if (style == Style.MONOCHROMATIC) {
- if (!monochromeEnabled) {
- continue
- }
- hasMonochrome = true
- monochromeBundleName = bundleName
+ // keep track of whether monochrome is included in preset colors to determine
+ // inclusion in wallpaper colors
+ var hasMonochrome = false
+ for (bundleName in bundleNames.take(maxPresetColors)) {
+ if (themeStyleEnabled) {
+ val styleName =
+ try {
+ getItemStringFromStub(COLOR_BUNDLE_STYLE_PREFIX, bundleName)
+ } catch (e: Resources.NotFoundException) {
+ null
+ }
+ val style =
+ try {
+ if (styleName != null) Style.valueOf(styleName) else Style.TONAL_SPOT
+ } catch (e: IllegalArgumentException) {
+ Style.TONAL_SPOT
}
- bundles.add(buildRevampedUIPreset(bundleName, index, style))
- } else {
- bundles.add(buildRevampedUIPreset(bundleName, index, null))
- }
-
- index++
- }
- if (!hasMonochrome) {
- monochromeBundleName = null
- }
- } else {
- for (bundleName in bundleNames.take(maxPresetColors)) {
- val builder = ColorBundle.Builder()
- builder.title = getItemStringFromStub(COLOR_BUNDLE_NAME_PREFIX, bundleName)
- builder.setIndex(index)
- val colorFromStub =
- getItemColorFromStub(COLOR_BUNDLE_MAIN_COLOR_PREFIX, bundleName)
- extractor.addPrimaryColor(builder, colorFromStub)
- extractor.addSecondaryColor(builder, colorFromStub)
- if (themeStyleEnabled) {
- val styleName =
- try {
- getItemStringFromStub(COLOR_BUNDLE_STYLE_PREFIX, bundleName)
- } catch (e: Resources.NotFoundException) {
- null
- }
- extractor.addColorStyle(builder, styleName)
- val style =
- try {
- if (styleName != null) Style.valueOf(styleName)
- else Style.TONAL_SPOT
- } catch (e: IllegalArgumentException) {
- Style.TONAL_SPOT
- }
- if (style == Style.MONOCHROMATIC && !monochromeEnabled) {
+ if (style == Style.MONOCHROMATIC) {
+ if (
+ !InjectorProvider.getInjector()
+ .getFlags()
+ .isMonochromaticThemeEnabled(mContext)
+ ) {
continue
}
-
- val darkColors =
- ColorScheme(colorFromStub, /* darkTheme= */ true, style)
- .getPresetColorPreview(colorFromStub)
- val lightColors =
- ColorScheme(colorFromStub, /* darkTheme= */ false, style)
- .getPresetColorPreview(colorFromStub)
- builder
- .setColorPrimaryDark(darkColors[0])
- .setColorSecondaryDark(darkColors[1])
- builder
- .setColorPrimaryLight(lightColors[0])
- .setColorSecondaryLight(lightColors[1])
+ hasMonochrome = true
+ monochromeBundleName = bundleName
}
-
- bundles.add(builder.build(mContext))
- index++
+ bundles.add(buildPreset(bundleName, index, style))
+ } else {
+ bundles.add(buildPreset(bundleName, index, null))
}
+
+ index++
+ }
+ if (!hasMonochrome) {
+ monochromeBundleName = null
}
colorBundles = bundles
}
- private fun buildRevampedUIPreset(
+ private fun buildPreset(
bundleName: String,
index: Int,
style: Style? = null,
@@ -554,12 +412,12 @@ class ColorProvider(private val context: Context, stubPackageName: String) :
when (style) {
Style.MONOCHROMATIC -> {
- darkColors = getRevampedUIDarkMonochromePreview(darkColorScheme)
- lightColors = getRevampedUILightMonochromePreview(lightColorScheme)
+ darkColors = getDarkMonochromePreview(darkColorScheme)
+ lightColors = getLightMonochromePreview(lightColorScheme)
}
else -> {
- darkColors = getRevampedUIPresetColorPreview(darkColorScheme, colorFromStub)
- lightColors = getRevampedUIPresetColorPreview(lightColorScheme, colorFromStub)
+ darkColors = getPresetColorPreview(darkColorScheme, colorFromStub)
+ lightColors = getPresetColorPreview(lightColorScheme, colorFromStub)
}
}
}
diff --git a/src/com/android/customization/model/color/ColorSeedOption.java b/src/com/android/customization/model/color/ColorSeedOption.java
deleted file mode 100644
index ed38049e..00000000
--- a/src/com/android/customization/model/color/ColorSeedOption.java
+++ /dev/null
@@ -1,252 +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.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PorterDuff.Mode;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.customization.model.color.ColorOptionsProvider.ColorSource;
-import com.android.systemui.monet.Style;
-import com.android.wallpaper.R;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Represents a seed color obtained from WallpaperColors, for the user to chose as their theming
- * option.
- */
-public class ColorSeedOption extends ColorOption {
-
- private final PreviewInfo mPreviewInfo;
- @ColorSource
- private final String mSource;
-
- @VisibleForTesting
- ColorSeedOption(String title, Map<String, String> overlayPackages, boolean isDefault,
- @ColorSource String source, Style style, int index, PreviewInfo previewInfo) {
- super(title, overlayPackages, isDefault, style, index);
- mSource = source;
- mPreviewInfo = previewInfo;
- }
-
- @Override
- public PreviewInfo getPreviewInfo() {
- return mPreviewInfo;
- }
-
- @Override
- public String getSource() {
- return mSource;
- }
-
- @Override
- public int getLayoutResId() {
- return R.layout.color_option;
- }
-
- @Override
- public void bindThumbnailTile(View view) {
- Resources res = view.getContext().getResources();
- @ColorInt int[] colors = mPreviewInfo.resolveColors(res);
-
- for (int i = 0; i < mPreviewColorIds.length; i++) {
- ImageView colorPreviewImageView = view.findViewById(mPreviewColorIds[i]);
- colorPreviewImageView.getDrawable().setColorFilter(colors[i], Mode.SRC);
- }
-
- view.setContentDescription(getContentDescription(view.getContext()));
- }
-
- @Override
- public CharSequence getContentDescription(Context context) {
- // Override because we want all options with the same description.
- return context.getString(R.string.wallpaper_color_title);
- }
-
- /**
- * The preview information of {@link ColorOption}
- */
- public static class PreviewInfo implements ColorOption.PreviewInfo {
- @ColorInt public int[] lightColors;
- @ColorInt public int[] darkColors;
-
- private PreviewInfo(@ColorInt int[] lightColors, @ColorInt int[] darkColors) {
- this.lightColors = lightColors;
- this.darkColors = darkColors;
- }
-
- /**
- * Returns the colors to be applied corresponding with the current
- * configuration's UI mode.
- * @param res resources to read to the UI mode configuration from
- * @return one of {@link #lightColors} or {@link #darkColors}
- */
- @ColorInt
- public int[] resolveColors(Resources res) {
- boolean night = (res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
- return night ? darkColors : lightColors;
- }
-
- /**
- * Returns the preview colors based on whether dark theme or light theme colors are
- * requested.
- * @param darkTheme if true, returns dark theme colors, otherwise returns light theme colors
- * @return one of {@link #lightColors} or {@link #darkColors}
- */
- @ColorInt
- public int[] resolveColors(boolean darkTheme) {
- return darkTheme ? darkColors : lightColors;
- }
- }
-
- /**
- * The builder of ColorSeedOption
- */
- public static class Builder {
- protected String mTitle;
- @ColorInt
- private int[] mLightColors;
- @ColorInt
- private int[] mDarkColors;
- @ColorSource
- private String mSource;
- private boolean mIsDefault;
- private Style mStyle = Style.TONAL_SPOT;
- private int mIndex;
- protected Map<String, String> mPackages = new HashMap<>();
-
- /**
- * Builds the ColorSeedOption
- * @return new {@link ColorOption} object
- */
- public ColorSeedOption build() {
- return new ColorSeedOption(mTitle, mPackages, mIsDefault, mSource, mStyle, mIndex,
- createPreviewInfo());
- }
-
- /**
- * Creates preview information
- * @return the {@link PreviewInfo} object
- */
- public PreviewInfo createPreviewInfo() {
- return new PreviewInfo(mLightColors, mDarkColors);
- }
-
- public Map<String, String> getPackages() {
- return Collections.unmodifiableMap(mPackages);
- }
-
- /**
- * Gets title of {@link ColorOption} object
- * @return title string
- */
- public String getTitle() {
- return mTitle;
- }
-
- /**
- * Sets title of bundle
- * @param title specified title
- * @return this of {@link ColorBundle.Builder}
- */
- public Builder setTitle(String title) {
- mTitle = title;
- return this;
- }
-
- /**
- * Sets the colors for preview in light mode
- * @param lightColors {@link ColorInt} colors for light mode
- * @return this of {@link Builder}
- */
- public Builder setLightColors(@ColorInt int[] lightColors) {
- mLightColors = lightColors;
- return this;
- }
-
- /**
- * Sets the colors for preview in light mode
- * @param darkColors {@link ColorInt} colors for light mode
- * @return this of {@link Builder}
- */
- public Builder setDarkColors(@ColorInt int[] darkColors) {
- mDarkColors = darkColors;
- return this;
- }
-
-
- /**
- * Sets overlay package for bundle
- * @param category the category of bundle
- * @param packageName tha name of package in the category
- * @return this of {@link Builder}
- */
- public Builder addOverlayPackage(String category, String packageName) {
- mPackages.put(category, packageName);
- return this;
- }
-
- /**
- * Sets the source of this color seed
- * @param source typically either {@link ColorOptionsProvider#COLOR_SOURCE_HOME} or
- * {@link ColorOptionsProvider#COLOR_SOURCE_LOCK}
- * @return this of {@link Builder}
- */
- public Builder setSource(@ColorSource String source) {
- mSource = source;
- return this;
- }
-
- /**
- * Sets the source of this color seed
- * @param style color style of {@link Style}
- * @return this of {@link Builder}
- */
- public Builder setStyle(Style style) {
- mStyle = style;
- return this;
- }
-
- /**
- * Sets color option index of seed
- * @param index color option index
- * @return this of {@link ColorBundle.Builder}
- */
- public Builder setIndex(int index) {
- mIndex = index;
- return this;
- }
-
- /**
- * Sets as default bundle
- * @return this of {@link Builder}
- */
- public Builder asDefault() {
- mIsDefault = true;
- return this;
- }
- }
-}
diff --git a/src/com/android/customization/model/color/ThemedWallpaperColorResources.kt b/src/com/android/customization/model/color/ThemedWallpaperColorResources.kt
new file mode 100644
index 00000000..906d9020
--- /dev/null
+++ b/src/com/android/customization/model/color/ThemedWallpaperColorResources.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.model.color
+
+import android.R
+import android.app.WallpaperColors
+import android.content.Context
+import android.provider.Settings
+import android.util.Log
+import com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_THEME_STYLE
+import com.android.systemui.monet.ColorScheme
+import com.android.systemui.monet.Style
+import org.json.JSONException
+import org.json.JSONObject
+
+class ThemedWallpaperColorResources(wallpaperColors: WallpaperColors, context: Context) :
+ WallpaperColorResources(wallpaperColors) {
+
+ init {
+ val wallpaperColorScheme =
+ ColorScheme(
+ wallpaperColors = wallpaperColors,
+ darkTheme = false,
+ style = fetchThemeStyleFromSetting(context)
+ )
+ addOverlayColor(wallpaperColorScheme.neutral1, R.color.system_neutral1_10)
+ addOverlayColor(wallpaperColorScheme.neutral2, R.color.system_neutral2_10)
+ addOverlayColor(wallpaperColorScheme.accent1, R.color.system_accent1_10)
+ addOverlayColor(wallpaperColorScheme.accent2, R.color.system_accent2_10)
+ addOverlayColor(wallpaperColorScheme.accent3, R.color.system_accent3_10)
+ }
+
+ private fun fetchThemeStyleFromSetting(context: Context): Style {
+ val overlayPackageJson =
+ Settings.Secure.getString(
+ context.contentResolver,
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ )
+ return if (!overlayPackageJson.isNullOrEmpty()) {
+ try {
+ val jsonObject = JSONObject(overlayPackageJson)
+ Style.valueOf(jsonObject.getString(OVERLAY_CATEGORY_THEME_STYLE))
+ } catch (e: (JSONException)) {
+ Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e)
+ Style.TONAL_SPOT
+ } catch (e: IllegalArgumentException) {
+ Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e)
+ Style.TONAL_SPOT
+ }
+ } else {
+ Style.TONAL_SPOT
+ }
+ }
+
+ companion object {
+ private const val TAG = "ThemedWallpaperColorResources"
+ }
+}
diff --git a/src/com/android/customization/model/grid/GridOption.java b/src/com/android/customization/model/grid/GridOption.java
index a5307c9e..347929c4 100644
--- a/src/com/android/customization/model/grid/GridOption.java
+++ b/src/com/android/customization/model/grid/GridOption.java
@@ -15,14 +15,11 @@
*/
package com.android.customization.model.grid;
-import android.content.Context;
-import android.graphics.PorterDuff.Mode;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.view.View;
-import android.widget.ImageView;
import androidx.annotation.Nullable;
@@ -30,7 +27,6 @@ import com.android.customization.model.CustomizationManager;
import com.android.customization.model.CustomizationOption;
import com.android.customization.widget.GridTileDrawable;
import com.android.wallpaper.R;
-import com.android.wallpaper.util.ResourceUtils;
/**
* Represents a grid layout option available in the current launcher.
@@ -94,21 +90,7 @@ public class GridOption implements CustomizationOption<GridOption>, Parcelable {
@Override
public void bindThumbnailTile(View view) {
- Context context = view.getContext();
-
- int colorFilter = ResourceUtils.getColorAttr(context,
- view.isActivated()
- ? (mIsCurrent
- ? android.R.attr.textColorPrimary
- : android.R.attr.textColorPrimaryInverse)
- : android.R.attr.textColorTertiary);
- mTileDrawable.setColorFilter(colorFilter, Mode.SRC_ATOP);
- ((ImageView) view.findViewById(R.id.grid_option_thumbnail))
- .setImageDrawable(mTileDrawable);
-
- int backgroundResource = view.isActivated() && !mIsCurrent
- ? R.drawable.option_border_new_selection : R.drawable.option_border;
- view.findViewById(R.id.option_tile).setBackgroundResource(backgroundResource);
+ // Do nothing. This function will no longer be used in the Revamped UI
}
@Override
diff --git a/src/com/android/customization/model/grid/GridOptionViewModel.java b/src/com/android/customization/model/grid/GridOptionViewModel.java
deleted file mode 100644
index 33fa8e179..00000000
--- a/src/com/android/customization/model/grid/GridOptionViewModel.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2021 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.grid;
-
-import androidx.lifecycle.SavedStateHandle;
-import androidx.lifecycle.ViewModel;
-
-/** The class to store status of the grid fragment view. */
-public class GridOptionViewModel extends ViewModel {
- private static final String SELECTED_OPTION_KEY = "selected_option";
- private static final String BOTTOM_ACTION_BAR_VISIBLE_KEY = "bottom_action_bar_visible";
-
- private SavedStateHandle mState;
-
- public GridOptionViewModel(SavedStateHandle savedStateHandle) {
- mState = savedStateHandle;
- }
-
- /** Gets selected {@link GridOption} from {@link SavedStateHandle} */
- public GridOption getSelectedOption() {
- return mState.get(SELECTED_OPTION_KEY);
- }
-
- /** Sets selected {@link GridOption} to {@link SavedStateHandle} */
- public void setSelectedOption(GridOption selectedOption) {
- mState.set(SELECTED_OPTION_KEY, selectedOption);
- }
-
- /** Gets bottom action bar visible from {@link SavedStateHandle} */
- public boolean getBottomActionBarVisible() {
- return mState.contains(BOTTOM_ACTION_BAR_VISIBLE_KEY)
- ? mState.get(BOTTOM_ACTION_BAR_VISIBLE_KEY)
- : false;
- }
-
- /** Sets bottom action bar visible to {@link SavedStateHandle} */
- public void setBottomActionBarVisible(boolean bottomActionBarVisible) {
- mState.set(BOTTOM_ACTION_BAR_VISIBLE_KEY, bottomActionBarVisible);
- }
-}
diff --git a/src/com/android/customization/model/grid/GridOptionsManager.java b/src/com/android/customization/model/grid/GridOptionsManager.java
index 78dbb5b2..bd24cf5f 100644
--- a/src/com/android/customization/model/grid/GridOptionsManager.java
+++ b/src/com/android/customization/model/grid/GridOptionsManager.java
@@ -27,7 +27,7 @@ import androidx.lifecycle.LiveData;
import com.android.customization.model.CustomizationManager;
import com.android.customization.module.CustomizationInjector;
-import com.android.customization.module.ThemesUserEventLogger;
+import com.android.customization.module.logging.ThemesUserEventLogger;
import com.android.wallpaper.R;
import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.util.PreviewUtils;
diff --git a/src/com/android/customization/model/mode/DarkModeSectionController.java b/src/com/android/customization/model/mode/DarkModeSectionController.java
index ebeaa567..71398297 100644
--- a/src/com/android/customization/model/mode/DarkModeSectionController.java
+++ b/src/com/android/customization/model/mode/DarkModeSectionController.java
@@ -39,6 +39,7 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
+import com.android.customization.module.logging.ThemesUserEventLogger;
import com.android.customization.picker.mode.DarkModeSectionView;
import com.android.wallpaper.R;
import com.android.wallpaper.model.CustomizationSectionController;
@@ -60,16 +61,19 @@ public class DarkModeSectionController implements
private Context mContext;
private DarkModeSectionView mDarkModeSectionView;
private final DarkModeSnapshotRestorer mSnapshotRestorer;
+ private final ThemesUserEventLogger mThemesUserEventLogger;
public DarkModeSectionController(
Context context,
Lifecycle lifecycle,
- DarkModeSnapshotRestorer snapshotRestorer) {
+ DarkModeSnapshotRestorer snapshotRestorer,
+ ThemesUserEventLogger themesUserEventLogger) {
mContext = context;
mLifecycle = lifecycle;
mPowerManager = context.getSystemService(PowerManager.class);
mLifecycle.addObserver(this);
mSnapshotRestorer = snapshotRestorer;
+ mThemesUserEventLogger = themesUserEventLogger;
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
@@ -137,6 +141,7 @@ public class DarkModeSectionController implements
mDarkModeSectionView.announceForAccessibility(
context.getString(R.string.mode_changed));
uiModeManager.setNightModeActivated(viewActivated);
+ mThemesUserEventLogger.logDarkThemeApplied(viewActivated);
mSnapshotRestorer.store(viewActivated);
},
/* delayMillis= */ shortDelay);
diff --git a/src/com/android/customization/model/theme/DefaultThemeProvider.java b/src/com/android/customization/model/theme/DefaultThemeProvider.java
deleted file mode 100644
index 89067c65..00000000
--- a/src/com/android/customization/model/theme/DefaultThemeProvider.java
+++ /dev/null
@@ -1,424 +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.model.theme;
-
-import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
-import static com.android.customization.model.ResourceConstants.ICONS_FOR_PREVIEW;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_ANDROID;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_LAUNCHER;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SETTINGS;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SYSUI;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_THEMEPICKER;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
-import static com.android.customization.model.ResourceConstants.SYSUI_PACKAGE;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-
-import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
-import com.android.customization.model.ResourcesApkProvider;
-import com.android.customization.model.theme.ThemeBundle.Builder;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo.ShapeAppIcon;
-import com.android.customization.model.theme.custom.CustomTheme;
-import com.android.customization.module.CustomizationPreferences;
-import com.android.wallpaper.R;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Default implementation of {@link ThemeBundleProvider} that reads Themes' overlays from a stub APK.
- */
-public class DefaultThemeProvider extends ResourcesApkProvider implements ThemeBundleProvider {
-
- private static final String TAG = "DefaultThemeProvider";
-
- private static final String THEMES_ARRAY = "themes";
- private static final String TITLE_PREFIX = "theme_title_";
- private static final String FONT_PREFIX = "theme_overlay_font_";
- private static final String COLOR_PREFIX = "theme_overlay_color_";
- private static final String SHAPE_PREFIX = "theme_overlay_shape_";
- private static final String ICON_ANDROID_PREFIX = "theme_overlay_icon_android_";
- private static final String ICON_LAUNCHER_PREFIX = "theme_overlay_icon_launcher_";
- private static final String ICON_THEMEPICKER_PREFIX = "theme_overlay_icon_themepicker_";
- private static final String ICON_SETTINGS_PREFIX = "theme_overlay_icon_settings_";
- private static final String ICON_SYSUI_PREFIX = "theme_overlay_icon_sysui_";
-
- private static final String DEFAULT_THEME_NAME= "default";
- private static final String THEME_TITLE_FIELD = "_theme_title";
- private static final String THEME_ID_FIELD = "_theme_id";
-
- private final OverlayThemeExtractor mOverlayProvider;
- private List<ThemeBundle> mThemes;
- private final CustomizationPreferences mCustomizationPreferences;
-
- public DefaultThemeProvider(Context context, CustomizationPreferences customizationPrefs) {
- super(context, context.getString(R.string.themes_stub_package));
- mOverlayProvider = new OverlayThemeExtractor(context);
- mCustomizationPreferences = customizationPrefs;
- }
-
- @Override
- public void fetch(OptionsFetchedListener<ThemeBundle> callback, boolean reload) {
- if (mThemes == null || reload) {
- mThemes = new ArrayList<>();
- loadAll();
- }
-
- if(callback != null) {
- callback.onOptionsLoaded(mThemes);
- }
- }
-
- @Override
- public boolean isAvailable() {
- return mOverlayProvider.isAvailable() && super.isAvailable();
- }
-
- private void loadAll() {
- // Add "Custom" option at the beginning.
- mThemes.add(new CustomTheme.Builder()
- .setId(CustomTheme.newId())
- .setTitle(mContext.getString(R.string.custom_theme))
- .build(mContext));
-
- addDefaultTheme();
-
- String[] themeNames = getItemsFromStub(THEMES_ARRAY);
-
- for (String themeName : themeNames) {
- // Default theme needs special treatment (see #addDefaultTheme())
- if (DEFAULT_THEME_NAME.equals(themeName)) {
- continue;
- }
- ThemeBundle.Builder builder = new Builder();
- try {
- builder.setTitle(mStubApkResources.getString(
- mStubApkResources.getIdentifier(TITLE_PREFIX + themeName,
- "string", mStubPackageName)));
-
- String shapeOverlayPackage = getOverlayPackage(SHAPE_PREFIX, themeName);
- mOverlayProvider.addShapeOverlay(builder, shapeOverlayPackage);
-
- String fontOverlayPackage = getOverlayPackage(FONT_PREFIX, themeName);
- mOverlayProvider.addFontOverlay(builder, fontOverlayPackage);
-
- String colorOverlayPackage = getOverlayPackage(COLOR_PREFIX, themeName);
- mOverlayProvider.addColorOverlay(builder, colorOverlayPackage);
-
- String iconAndroidOverlayPackage = getOverlayPackage(ICON_ANDROID_PREFIX,
- themeName);
-
- mOverlayProvider.addAndroidIconOverlay(builder, iconAndroidOverlayPackage);
-
- String iconSysUiOverlayPackage = getOverlayPackage(ICON_SYSUI_PREFIX, themeName);
-
- mOverlayProvider.addSysUiIconOverlay(builder, iconSysUiOverlayPackage);
-
- String iconLauncherOverlayPackage = getOverlayPackage(ICON_LAUNCHER_PREFIX,
- themeName);
- mOverlayProvider.addNoPreviewIconOverlay(builder, iconLauncherOverlayPackage);
-
- String iconThemePickerOverlayPackage = getOverlayPackage(ICON_THEMEPICKER_PREFIX,
- themeName);
- mOverlayProvider.addNoPreviewIconOverlay(builder,
- iconThemePickerOverlayPackage);
-
- String iconSettingsOverlayPackage = getOverlayPackage(ICON_SETTINGS_PREFIX,
- themeName);
-
- mOverlayProvider.addNoPreviewIconOverlay(builder, iconSettingsOverlayPackage);
-
- mThemes.add(builder.build(mContext));
- } catch (NameNotFoundException | NotFoundException e) {
- Log.w(TAG, String.format("Couldn't load part of theme %s, will skip it", themeName),
- e);
- }
- }
-
- addCustomThemes();
- }
-
- /**
- * Default theme requires different treatment: if there are overlay packages specified in the
- * stub apk, we'll use those, otherwise we'll get the System default values. But we cannot skip
- * the default theme.
- */
- private void addDefaultTheme() {
- ThemeBundle.Builder builder = new Builder().asDefault();
-
- int titleId = mStubApkResources.getIdentifier(TITLE_PREFIX + DEFAULT_THEME_NAME,
- "string", mStubPackageName);
- if (titleId > 0) {
- builder.setTitle(mStubApkResources.getString(titleId));
- } else {
- builder.setTitle(mContext.getString(R.string.default_theme_title));
- }
-
- try {
- String colorOverlayPackage = getOverlayPackage(COLOR_PREFIX, DEFAULT_THEME_NAME);
- mOverlayProvider.addColorOverlay(builder, colorOverlayPackage);
- } catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG, "Didn't find color overlay for default theme, will use system default");
- mOverlayProvider.addSystemDefaultColor(builder);
- }
-
- try {
- String fontOverlayPackage = getOverlayPackage(FONT_PREFIX, DEFAULT_THEME_NAME);
- mOverlayProvider.addFontOverlay(builder, fontOverlayPackage);
- } catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG, "Didn't find font overlay for default theme, will use system default");
- mOverlayProvider.addSystemDefaultFont(builder);
- }
-
- try {
- String shapeOverlayPackage = getOverlayPackage(SHAPE_PREFIX, DEFAULT_THEME_NAME);
- mOverlayProvider.addShapeOverlay(builder ,shapeOverlayPackage, false);
- } catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG, "Didn't find shape overlay for default theme, will use system default");
- mOverlayProvider.addSystemDefaultShape(builder);
- }
-
- List<ShapeAppIcon> icons = new ArrayList<>();
- for (String packageName : mOverlayProvider.getShapePreviewIconPackages()) {
- Drawable icon = null;
- CharSequence name = null;
- try {
- icon = mContext.getPackageManager().getApplicationIcon(packageName);
- ApplicationInfo appInfo = mContext.getPackageManager()
- .getApplicationInfo(packageName, /* flag= */ 0);
- name = mContext.getPackageManager().getApplicationLabel(appInfo);
- } catch (NameNotFoundException e) {
- Log.d(TAG, "Couldn't find app " + packageName + ", won't use it for icon shape"
- + "preview");
- } finally {
- if (icon != null && !TextUtils.isEmpty(name)) {
- icons.add(new ShapeAppIcon(icon, name));
- }
- }
- }
- builder.setShapePreviewIcons(icons);
-
- try {
- String iconAndroidOverlayPackage = getOverlayPackage(ICON_ANDROID_PREFIX,
- DEFAULT_THEME_NAME);
- mOverlayProvider.addAndroidIconOverlay(builder, iconAndroidOverlayPackage);
- } catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG, "Didn't find Android icons overlay for default theme, using system default");
- mOverlayProvider.addSystemDefaultIcons(builder, ANDROID_PACKAGE, ICONS_FOR_PREVIEW);
- }
-
- try {
- String iconSysUiOverlayPackage = getOverlayPackage(ICON_SYSUI_PREFIX,
- DEFAULT_THEME_NAME);
- mOverlayProvider.addSysUiIconOverlay(builder, iconSysUiOverlayPackage);
- } catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG,
- "Didn't find SystemUi icons overlay for default theme, using system default");
- mOverlayProvider.addSystemDefaultIcons(builder, SYSUI_PACKAGE, ICONS_FOR_PREVIEW);
- }
-
- mThemes.add(builder.build(mContext));
- }
-
- @Override
- public void storeCustomTheme(CustomTheme theme) {
- if (mThemes == null) {
- fetch(options -> {
- addCustomThemeAndStore(theme);
- }, false);
- } else {
- addCustomThemeAndStore(theme);
- }
- }
-
- private void addCustomThemeAndStore(CustomTheme theme) {
- if (!mThemes.contains(theme)) {
- mThemes.add(theme);
- } else {
- mThemes.replaceAll(t -> theme.equals(t) ? theme : t);
- }
- JSONArray themesArray = new JSONArray();
- mThemes.stream()
- .filter(themeBundle -> themeBundle instanceof CustomTheme
- && !themeBundle.getPackagesByCategory().isEmpty())
- .forEachOrdered(themeBundle -> addThemeBundleToArray(themesArray, themeBundle));
- mCustomizationPreferences.storeCustomThemes(themesArray.toString());
- }
-
- private void addThemeBundleToArray(JSONArray themesArray, ThemeBundle themeBundle) {
- JSONObject jsonPackages = themeBundle.getJsonPackages(false);
- try {
- jsonPackages.put(THEME_TITLE_FIELD, themeBundle.getTitle());
- if (themeBundle instanceof CustomTheme) {
- jsonPackages.put(THEME_ID_FIELD, ((CustomTheme)themeBundle).getId());
- }
- } catch (JSONException e) {
- Log.w("Exception saving theme's title", e);
- }
- themesArray.put(jsonPackages);
- }
-
- @Override
- public void removeCustomTheme(CustomTheme theme) {
- JSONArray themesArray = new JSONArray();
- mThemes.stream()
- .filter(themeBundle -> themeBundle instanceof CustomTheme
- && ((CustomTheme) themeBundle).isDefined())
- .forEachOrdered(customTheme -> {
- if (!customTheme.equals(theme)) {
- addThemeBundleToArray(themesArray, customTheme);
- }
- });
- mCustomizationPreferences.storeCustomThemes(themesArray.toString());
- }
-
- private void addCustomThemes() {
- String serializedThemes = mCustomizationPreferences.getSerializedCustomThemes();
- int customThemesCount = 0;
- if (!TextUtils.isEmpty(serializedThemes)) {
- try {
- JSONArray customThemes = new JSONArray(serializedThemes);
- for (int i = 0; i < customThemes.length(); i++) {
- JSONObject jsonTheme = customThemes.getJSONObject(i);
- CustomTheme.Builder builder = new CustomTheme.Builder();
- try {
- convertJsonToBuilder(jsonTheme, builder);
- } catch (NameNotFoundException | NotFoundException e) {
- Log.i(TAG, "Couldn't parse serialized custom theme", e);
- builder = null;
- }
- if (builder != null) {
- if (TextUtils.isEmpty(builder.getTitle())) {
- builder.setTitle(mContext.getString(R.string.custom_theme_title,
- customThemesCount + 1));
- }
- mThemes.add(builder.build(mContext));
- } else {
- Log.w(TAG, "Couldn't read stored custom theme, resetting");
- mThemes.add(new CustomTheme.Builder()
- .setId(CustomTheme.newId())
- .setTitle(mContext.getString(
- R.string.custom_theme_title, customThemesCount + 1))
- .build(mContext));
- }
- customThemesCount++;
- }
- } catch (JSONException e) {
- Log.w(TAG, "Couldn't read stored custom theme, resetting", e);
- mThemes.add(new CustomTheme.Builder()
- .setId(CustomTheme.newId())
- .setTitle(mContext.getString(
- R.string.custom_theme_title, customThemesCount + 1))
- .build(mContext));
- }
- }
- }
-
- @Nullable
- @Override
- public ThemeBundle.Builder parseThemeBundle(String serializedTheme) throws JSONException {
- JSONObject theme = new JSONObject(serializedTheme);
- try {
- ThemeBundle.Builder builder = new ThemeBundle.Builder();
- convertJsonToBuilder(theme, builder);
- return builder;
- } catch (NameNotFoundException | NotFoundException e) {
- Log.i(TAG, "Couldn't parse serialized custom theme", e);
- return null;
- }
- }
-
- @Nullable
- @Override
- public CustomTheme.Builder parseCustomTheme(String serializedTheme) throws JSONException {
- JSONObject theme = new JSONObject(serializedTheme);
- try {
- CustomTheme.Builder builder = new CustomTheme.Builder();
- convertJsonToBuilder(theme, builder);
- return builder;
- } catch (NameNotFoundException | NotFoundException e) {
- Log.i(TAG, "Couldn't parse serialized custom theme", e);
- return null;
- }
- }
-
- private void convertJsonToBuilder(JSONObject theme, ThemeBundle.Builder builder)
- throws JSONException, NameNotFoundException, NotFoundException {
- Map<String, String> customPackages = new HashMap<>();
- Iterator<String> keysIterator = theme.keys();
-
- while (keysIterator.hasNext()) {
- String category = keysIterator.next();
- customPackages.put(category, theme.getString(category));
- }
- mOverlayProvider.addShapeOverlay(builder,
- customPackages.get(OVERLAY_CATEGORY_SHAPE));
- mOverlayProvider.addFontOverlay(builder,
- customPackages.get(OVERLAY_CATEGORY_FONT));
- mOverlayProvider.addColorOverlay(builder,
- customPackages.get(OVERLAY_CATEGORY_COLOR));
- mOverlayProvider.addAndroidIconOverlay(builder,
- customPackages.get(OVERLAY_CATEGORY_ICON_ANDROID));
- mOverlayProvider.addSysUiIconOverlay(builder,
- customPackages.get(OVERLAY_CATEGORY_ICON_SYSUI));
- mOverlayProvider.addNoPreviewIconOverlay(builder,
- customPackages.get(OVERLAY_CATEGORY_ICON_SETTINGS));
- mOverlayProvider.addNoPreviewIconOverlay(builder,
- customPackages.get(OVERLAY_CATEGORY_ICON_LAUNCHER));
- mOverlayProvider.addNoPreviewIconOverlay(builder,
- customPackages.get(OVERLAY_CATEGORY_ICON_THEMEPICKER));
- if (theme.has(THEME_TITLE_FIELD)) {
- builder.setTitle(theme.getString(THEME_TITLE_FIELD));
- }
- if (builder instanceof CustomTheme.Builder && theme.has(THEME_ID_FIELD)) {
- ((CustomTheme.Builder) builder).setId(theme.getString(THEME_ID_FIELD));
- }
- }
-
- @Override
- public ThemeBundle findEquivalent(ThemeBundle other) {
- if (mThemes == null) {
- return null;
- }
- for (ThemeBundle theme : mThemes) {
- if (theme.isEquivalent(other)) {
- return theme;
- }
- }
- return null;
- }
-
- private String getOverlayPackage(String prefix, String themeName) {
- return getItemStringFromStub(prefix, themeName);
- }
-}
diff --git a/src/com/android/customization/model/theme/OverlayThemeExtractor.java b/src/com/android/customization/model/theme/OverlayThemeExtractor.java
deleted file mode 100644
index 816176e7..00000000
--- a/src/com/android/customization/model/theme/OverlayThemeExtractor.java
+++ /dev/null
@@ -1,293 +0,0 @@
-package com.android.customization.model.theme;
-
-import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
-import static com.android.customization.model.ResourceConstants.ICONS_FOR_PREVIEW;
-import static com.android.customization.model.ResourceConstants.SETTINGS_PACKAGE;
-import static com.android.customization.model.ResourceConstants.SYSUI_PACKAGE;
-
-import android.content.Context;
-import android.content.om.OverlayInfo;
-import android.content.om.OverlayManager;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.Dimension;
-import androidx.annotation.Nullable;
-
-import com.android.customization.model.ResourceConstants;
-import com.android.customization.model.theme.ThemeBundle.Builder;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo.ShapeAppIcon;
-import com.android.wallpaper.R;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Consumer;
-
-class OverlayThemeExtractor {
-
- private static final String TAG = "OverlayThemeExtractor";
-
- private final Context mContext;
- private final Map<String, OverlayInfo> mOverlayInfos = new HashMap<>();
- // List of packages
- private final String[] mShapePreviewIconPackages;
-
- OverlayThemeExtractor(Context context) {
- mContext = context;
- OverlayManager om = context.getSystemService(OverlayManager.class);
- if (om != null) {
- Consumer<OverlayInfo> addToMap = overlayInfo -> mOverlayInfos.put(
- overlayInfo.getPackageName(), overlayInfo);
-
- UserHandle user = UserHandle.of(UserHandle.myUserId());
- om.getOverlayInfosForTarget(ANDROID_PACKAGE, user).forEach(addToMap);
- om.getOverlayInfosForTarget(SYSUI_PACKAGE, user).forEach(addToMap);
- om.getOverlayInfosForTarget(SETTINGS_PACKAGE, user).forEach(addToMap);
- om.getOverlayInfosForTarget(ResourceConstants.getLauncherPackage(context), user)
- .forEach(addToMap);
- om.getOverlayInfosForTarget(context.getPackageName(), user).forEach(addToMap);
- }
- mShapePreviewIconPackages = context.getResources().getStringArray(
- R.array.icon_shape_preview_packages);
- }
-
- boolean isAvailable() {
- return !mOverlayInfos.isEmpty();
- }
-
- void addColorOverlay(Builder builder, String colorOverlayPackage)
- throws NameNotFoundException {
- if (!TextUtils.isEmpty(colorOverlayPackage)) {
- builder.addOverlayPackage(getOverlayCategory(colorOverlayPackage),
- colorOverlayPackage)
- .setColorAccentLight(loadColor(ResourceConstants.ACCENT_COLOR_LIGHT_NAME,
- colorOverlayPackage))
- .setColorAccentDark(loadColor(ResourceConstants.ACCENT_COLOR_DARK_NAME,
- colorOverlayPackage));
- } else {
- addSystemDefaultColor(builder);
- }
- }
-
- void addShapeOverlay(Builder builder, String shapeOverlayPackage)
- throws NameNotFoundException {
- addShapeOverlay(builder, shapeOverlayPackage, true);
- }
-
- void addShapeOverlay(Builder builder, String shapeOverlayPackage, boolean addPreview)
- throws NameNotFoundException {
- if (!TextUtils.isEmpty(shapeOverlayPackage)) {
- builder.addOverlayPackage(getOverlayCategory(shapeOverlayPackage),
- shapeOverlayPackage)
- .setShapePath(
- loadString(ResourceConstants.CONFIG_ICON_MASK, shapeOverlayPackage))
- .setBottomSheetCornerRadius(
- loadDimen(ResourceConstants.CONFIG_CORNERRADIUS, shapeOverlayPackage));
- } else {
- addSystemDefaultShape(builder);
- }
- if (addPreview) {
- addShapePreviewIcons(builder);
- }
- }
-
- private void addShapePreviewIcons(Builder builder) {
- List<ShapeAppIcon> icons = new ArrayList<>();
- for (String packageName : mShapePreviewIconPackages) {
- Drawable icon = null;
- CharSequence name = null;
- try {
- icon = mContext.getPackageManager().getApplicationIcon(packageName);
- // Add the shape icon app name.
- ApplicationInfo appInfo = mContext.getPackageManager()
- .getApplicationInfo(packageName, /* flag= */ 0);
- name = mContext.getPackageManager().getApplicationLabel(appInfo);
- } catch (NameNotFoundException e) {
- Log.d(TAG, "Couldn't find app " + packageName
- + ", won't use it for icon shape preview");
- } finally {
- if (icon != null && !TextUtils.isEmpty(name)) {
- icons.add(new ShapeAppIcon(icon, name));
- }
- }
- }
- builder.setShapePreviewIcons(icons);
- }
-
- void addNoPreviewIconOverlay(Builder builder, String overlayPackage) {
- if (!TextUtils.isEmpty(overlayPackage)) {
- builder.addOverlayPackage(getOverlayCategory(overlayPackage),
- overlayPackage);
- }
- }
-
- void addSysUiIconOverlay(Builder builder, String iconSysUiOverlayPackage)
- throws NameNotFoundException {
- if (!TextUtils.isEmpty(iconSysUiOverlayPackage)) {
- addIconOverlay(builder, iconSysUiOverlayPackage);
- }
- }
-
- void addAndroidIconOverlay(Builder builder, String iconAndroidOverlayPackage)
- throws NameNotFoundException {
- if (!TextUtils.isEmpty(iconAndroidOverlayPackage)) {
- addIconOverlay(builder, iconAndroidOverlayPackage, ICONS_FOR_PREVIEW);
- } else {
- addSystemDefaultIcons(builder, ANDROID_PACKAGE, ICONS_FOR_PREVIEW);
- }
- }
-
- void addIconOverlay(Builder builder, String packageName, String... previewIcons)
- throws NameNotFoundException {
- builder.addOverlayPackage(getOverlayCategory(packageName), packageName);
- for (String iconName : previewIcons) {
- builder.addIcon(loadIconPreviewDrawable(iconName, packageName, false));
- }
- }
-
- void addFontOverlay(Builder builder, String fontOverlayPackage)
- throws NameNotFoundException {
- if (!TextUtils.isEmpty(fontOverlayPackage)) {
- builder.addOverlayPackage(getOverlayCategory(fontOverlayPackage),
- fontOverlayPackage)
- .setBodyFontFamily(loadTypeface(
- ResourceConstants.CONFIG_BODY_FONT_FAMILY,
- fontOverlayPackage))
- .setHeadlineFontFamily(loadTypeface(
- ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY,
- fontOverlayPackage));
- } else {
- addSystemDefaultFont(builder);
- }
- }
-
- void addSystemDefaultIcons(Builder builder, String packageName,
- String... previewIcons) {
- try {
- for (String iconName : previewIcons) {
- builder.addIcon(loadIconPreviewDrawable(iconName, packageName, true));
- }
- } catch (NameNotFoundException | NotFoundException e) {
- Log.w(TAG, "Didn't find android package icons, will skip preview", e);
- }
- }
-
- void addSystemDefaultShape(Builder builder) {
- Resources system = Resources.getSystem();
- String iconMaskPath = system.getString(
- system.getIdentifier(ResourceConstants.CONFIG_ICON_MASK,
- "string", ResourceConstants.ANDROID_PACKAGE));
- builder.setShapePath(iconMaskPath)
- .setBottomSheetCornerRadius(
- system.getDimensionPixelOffset(
- system.getIdentifier(ResourceConstants.CONFIG_CORNERRADIUS,
- "dimen", ResourceConstants.ANDROID_PACKAGE)));
- }
-
- void addSystemDefaultColor(Builder builder) {
- Resources system = Resources.getSystem();
- int colorAccentLight = system.getColor(
- system.getIdentifier(ResourceConstants.ACCENT_COLOR_LIGHT_NAME, "color",
- ResourceConstants.ANDROID_PACKAGE), null);
- builder.setColorAccentLight(colorAccentLight);
-
- int colorAccentDark = system.getColor(
- system.getIdentifier(ResourceConstants.ACCENT_COLOR_DARK_NAME, "color",
- ResourceConstants.ANDROID_PACKAGE), null);
- builder.setColorAccentDark(colorAccentDark);
- }
-
- void addSystemDefaultFont(Builder builder) {
- Resources system = Resources.getSystem();
- String headlineFontFamily = system.getString(system.getIdentifier(
- ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY, "string",
- ResourceConstants.ANDROID_PACKAGE));
- String bodyFontFamily = system.getString(system.getIdentifier(
- ResourceConstants.CONFIG_BODY_FONT_FAMILY,
- "string", ResourceConstants.ANDROID_PACKAGE));
- builder.setHeadlineFontFamily(Typeface.create(headlineFontFamily, Typeface.NORMAL))
- .setBodyFontFamily(Typeface.create(bodyFontFamily, Typeface.NORMAL));
- }
-
- Typeface loadTypeface(String configName, String fontOverlayPackage)
- throws NameNotFoundException, NotFoundException {
-
- // TODO(santie): check for font being present in system
-
- Resources overlayRes = mContext.getPackageManager()
- .getResourcesForApplication(fontOverlayPackage);
-
- String fontFamily = overlayRes.getString(overlayRes.getIdentifier(configName,
- "string", fontOverlayPackage));
- return Typeface.create(fontFamily, Typeface.NORMAL);
- }
-
- int loadColor(String colorName, String colorPackage)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes = mContext.getPackageManager()
- .getResourcesForApplication(colorPackage);
- return overlayRes.getColor(overlayRes.getIdentifier(colorName, "color", colorPackage),
- null);
- }
-
- String loadString(String stringName, String packageName)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes =
- mContext.getPackageManager().getResourcesForApplication(
- packageName);
- return overlayRes.getString(overlayRes.getIdentifier(stringName, "string", packageName));
- }
-
- @Dimension
- int loadDimen(String dimenName, String packageName)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes =
- mContext.getPackageManager().getResourcesForApplication(
- packageName);
- return overlayRes.getDimensionPixelOffset(overlayRes.getIdentifier(
- dimenName, "dimen", packageName));
- }
-
- boolean loadBoolean(String booleanName, String packageName)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes =
- mContext.getPackageManager().getResourcesForApplication(
- packageName);
- return overlayRes.getBoolean(overlayRes.getIdentifier(
- booleanName, "boolean", packageName));
- }
-
- Drawable loadIconPreviewDrawable(String drawableName, String packageName,
- boolean fromSystem) throws NameNotFoundException, NotFoundException {
-
- Resources packageRes =
- mContext.getPackageManager().getResourcesForApplication(
- packageName);
- Resources res = fromSystem ? Resources.getSystem() : packageRes;
- return res.getDrawable(
- packageRes.getIdentifier(drawableName, "drawable", packageName), null);
- }
-
- @Nullable
- String getOverlayCategory(String packageName) {
- OverlayInfo info = mOverlayInfos.get(packageName);
- return info != null ? info.getCategory() : null;
- }
-
- String[] getShapePreviewIconPackages() {
- return mShapePreviewIconPackages;
- }
-} \ No newline at end of file
diff --git a/src/com/android/customization/model/theme/ThemeBundle.java b/src/com/android/customization/model/theme/ThemeBundle.java
deleted file mode 100644
index 3a32f257..00000000
--- a/src/com/android/customization/model/theme/ThemeBundle.java
+++ /dev/null
@@ -1,432 +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.model.theme;
-
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_ANDROID;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
-import static com.android.customization.model.ResourceConstants.PATH_SIZE;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Path;
-import android.graphics.Typeface;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.PathShape;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.Dimension;
-import androidx.annotation.Nullable;
-import androidx.core.graphics.PathParser;
-
-import com.android.customization.model.CustomizationManager;
-import com.android.customization.model.CustomizationOption;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo.ShapeAppIcon;
-import com.android.customization.widget.DynamicAdaptiveIconDrawable;
-import com.android.wallpaper.R;
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.BitmapCachingAsset;
-import com.android.wallpaper.model.WallpaperInfo;
-import com.android.wallpaper.util.ResourceUtils;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Represents a Theme component available in the system as a "persona" bundle.
- * Note that in this context a Theme is not related to Android's Styles, but it's rather an
- * abstraction representing a series of overlays to be applied to the system.
- */
-public class ThemeBundle implements CustomizationOption<ThemeBundle> {
-
- private static final String TAG = "ThemeBundle";
- private final static String EMPTY_JSON = "{}";
- private final static String TIMESTAMP_FIELD = "_applied_timestamp";
-
- private final String mTitle;
- private final PreviewInfo mPreviewInfo;
- private final boolean mIsDefault;
- protected final Map<String, String> mPackagesByCategory;
- private WallpaperInfo mOverrideWallpaper;
- private Asset mOverrideWallpaperAsset;
- private CharSequence mContentDescription;
-
- protected ThemeBundle(String title, Map<String, String> overlayPackages,
- boolean isDefault, PreviewInfo previewInfo) {
- mTitle = title;
- mIsDefault = isDefault;
- mPreviewInfo = previewInfo;
- mPackagesByCategory = Collections.unmodifiableMap(removeNullValues(overlayPackages));
- }
-
- @Override
- public String getTitle() {
- return mTitle;
- }
-
- @Override
- public void bindThumbnailTile(View view) {
- Resources res = view.getContext().getResources();
-
- ((TextView) view.findViewById(R.id.theme_option_font)).setTypeface(
- mPreviewInfo.headlineFontFamily);
- if (mPreviewInfo.shapeDrawable != null) {
- ((ShapeDrawable) mPreviewInfo.shapeDrawable).getPaint().setColor(
- mPreviewInfo.resolveAccentColor(res));
- ((ImageView) view.findViewById(R.id.theme_option_shape)).setImageDrawable(
- mPreviewInfo.shapeDrawable);
- }
- if (!mPreviewInfo.icons.isEmpty()) {
- Drawable icon = mPreviewInfo.icons.get(0).getConstantState().newDrawable().mutate();
- icon.setTint(ResourceUtils.getColorAttr(
- view.getContext(), android.R.attr.textColorSecondary));
- ((ImageView) view.findViewById(R.id.theme_option_icon)).setImageDrawable(
- icon);
- }
- view.setContentDescription(getContentDescription(view.getContext()));
- }
-
- @Override
- public boolean isActive(CustomizationManager<ThemeBundle> manager) {
- ThemeManager themeManager = (ThemeManager) manager;
-
- if (mIsDefault) {
- String serializedOverlays = themeManager.getStoredOverlays();
- return TextUtils.isEmpty(serializedOverlays) || EMPTY_JSON.equals(serializedOverlays);
- } else {
- Map<String, String> currentOverlays = themeManager.getCurrentOverlays();
- return mPackagesByCategory.equals(currentOverlays);
- }
- }
-
- @Override
- public int getLayoutResId() {
- return R.layout.theme_option;
- }
-
- /**
- * This is similar to #equals() but it only compares this theme's packages with the other, that
- * is, it will return true if applying this theme has the same effect of applying the given one.
- */
- public boolean isEquivalent(ThemeBundle other) {
- if (other == null) {
- return false;
- }
- if (mIsDefault) {
- return other.isDefault() || TextUtils.isEmpty(other.getSerializedPackages())
- || EMPTY_JSON.equals(other.getSerializedPackages());
- }
- // Map#equals ensures keys and values are compared.
- return mPackagesByCategory.equals(other.mPackagesByCategory);
- }
-
- public PreviewInfo getPreviewInfo() {
- return mPreviewInfo;
- }
-
- public void setOverrideThemeWallpaper(WallpaperInfo homeWallpaper) {
- mOverrideWallpaper = homeWallpaper;
- mOverrideWallpaperAsset = null;
- }
-
- private Asset getOverrideWallpaperAsset(Context context) {
- if (mOverrideWallpaperAsset == null) {
- mOverrideWallpaperAsset = new BitmapCachingAsset(context,
- mOverrideWallpaper.getThumbAsset(context));
- }
- return mOverrideWallpaperAsset;
- }
-
- boolean isDefault() {
- return mIsDefault;
- }
-
- public Map<String, String> getPackagesByCategory() {
- return mPackagesByCategory;
- }
-
- public String getSerializedPackages() {
- return getJsonPackages(false).toString();
- }
-
- public String getSerializedPackagesWithTimestamp() {
- return getJsonPackages(true).toString();
- }
-
- JSONObject getJsonPackages(boolean insertTimestamp) {
- if (isDefault()) {
- return new JSONObject();
- }
- JSONObject json = new JSONObject(mPackagesByCategory);
- // Remove items with null values to avoid deserialization issues.
- removeNullValues(json);
- if (insertTimestamp) {
- try {
- json.put(TIMESTAMP_FIELD, System.currentTimeMillis());
- } catch (JSONException e) {
- Log.e(TAG, "Couldn't add timestamp to serialized themebundle");
- }
- }
- return json;
- }
-
- private void removeNullValues(JSONObject json) {
- Iterator<String> keys = json.keys();
- Set<String> keysToRemove = new HashSet<>();
- while(keys.hasNext()) {
- String key = keys.next();
- if (json.isNull(key)) {
- keysToRemove.add(key);
- }
- }
- for (String key : keysToRemove) {
- json.remove(key);
- }
- }
-
- private Map<String, String> removeNullValues(Map<String, String> map) {
- return map.entrySet()
- .stream()
- .filter(entry -> entry.getValue() != null)
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
- }
-
- protected CharSequence getContentDescription(Context context) {
- if (mContentDescription == null) {
- CharSequence defaultName = context.getString(R.string.default_theme_title);
- if (isDefault()) {
- mContentDescription = defaultName;
- } else {
- PackageManager pm = context.getPackageManager();
- CharSequence fontName = getOverlayName(pm, OVERLAY_CATEGORY_FONT);
- CharSequence iconName = getOverlayName(pm, OVERLAY_CATEGORY_ICON_ANDROID);
- CharSequence shapeName = getOverlayName(pm, OVERLAY_CATEGORY_SHAPE);
- CharSequence colorName = getOverlayName(pm, OVERLAY_CATEGORY_COLOR);
- mContentDescription = context.getString(R.string.theme_description,
- TextUtils.isEmpty(fontName) ? defaultName : fontName,
- TextUtils.isEmpty(iconName) ? defaultName : iconName,
- TextUtils.isEmpty(shapeName) ? defaultName : shapeName,
- TextUtils.isEmpty(colorName) ? defaultName : colorName);
- }
- }
- return mContentDescription;
- }
-
- private CharSequence getOverlayName(PackageManager pm, String overlayCategoryFont) {
- try {
- return pm.getApplicationInfo(
- mPackagesByCategory.get(overlayCategoryFont), 0).loadLabel(pm);
- } catch (PackageManager.NameNotFoundException e) {
- return "";
- }
- }
-
- public static class PreviewInfo {
- public final Typeface bodyFontFamily;
- public final Typeface headlineFontFamily;
- @ColorInt public final int colorAccentLight;
- @ColorInt public final int colorAccentDark;
- public final List<Drawable> icons;
- public final Drawable shapeDrawable;
- public final List<ShapeAppIcon> shapeAppIcons;
- @Dimension public final int bottomSheeetCornerRadius;
-
- /** A class to represent an App icon and its name. */
- public static class ShapeAppIcon {
- private Drawable mIconDrawable;
- private CharSequence mAppName;
-
- public ShapeAppIcon(Drawable icon, CharSequence appName) {
- mIconDrawable = icon;
- mAppName = appName;
- }
-
- /** Returns a copy of app icon drawable. */
- public Drawable getDrawableCopy() {
- return mIconDrawable.getConstantState().newDrawable().mutate();
- }
-
- /** Returns the app name. */
- public CharSequence getAppName() {
- return mAppName;
- }
- }
-
- private PreviewInfo(Context context, Typeface bodyFontFamily, Typeface headlineFontFamily,
- int colorAccentLight, int colorAccentDark, List<Drawable> icons,
- Drawable shapeDrawable, @Dimension int cornerRadius,
- List<ShapeAppIcon> shapeAppIcons) {
- this.bodyFontFamily = bodyFontFamily;
- this.headlineFontFamily = headlineFontFamily;
- this.colorAccentLight = colorAccentLight;
- this.colorAccentDark = colorAccentDark;
- this.icons = icons;
- this.shapeDrawable = shapeDrawable;
- this.bottomSheeetCornerRadius = cornerRadius;
- this.shapeAppIcons = shapeAppIcons;
- }
-
- /**
- * Returns the accent color to be applied corresponding with the current configuration's
- * UI mode.
- * @return one of {@link #colorAccentDark} or {@link #colorAccentLight}
- */
- @ColorInt
- public int resolveAccentColor(Resources res) {
- return (res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES ? colorAccentDark : colorAccentLight;
- }
- }
-
- public static class Builder {
- protected String mTitle;
- private Typeface mBodyFontFamily;
- private Typeface mHeadlineFontFamily;
- @ColorInt private int mColorAccentLight = -1;
- @ColorInt private int mColorAccentDark = -1;
- private List<Drawable> mIcons = new ArrayList<>();
- private String mPathString;
- private Path mShapePath;
- private boolean mIsDefault;
- @Dimension private int mCornerRadius;
- protected Map<String, String> mPackages = new HashMap<>();
- private List<ShapeAppIcon> mAppIcons = new ArrayList<>();
-
- public ThemeBundle build(Context context) {
- return new ThemeBundle(mTitle, mPackages, mIsDefault, createPreviewInfo(context));
- }
-
- public PreviewInfo createPreviewInfo(Context context) {
- ShapeDrawable shapeDrawable = null;
- List<ShapeAppIcon> shapeIcons = new ArrayList<>();
- Path path = mShapePath;
- if (!TextUtils.isEmpty(mPathString)) {
- path = PathParser.createPathFromPathData(mPathString);
- }
- if (path != null) {
- PathShape shape = new PathShape(path, PATH_SIZE, PATH_SIZE);
- shapeDrawable = new ShapeDrawable(shape);
- shapeDrawable.setIntrinsicHeight((int) PATH_SIZE);
- shapeDrawable.setIntrinsicWidth((int) PATH_SIZE);
- for (ShapeAppIcon icon : mAppIcons) {
- Drawable drawable = icon.mIconDrawable;
- if (drawable instanceof AdaptiveIconDrawable) {
- AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) drawable;
- shapeIcons.add(new ShapeAppIcon(
- new DynamicAdaptiveIconDrawable(adaptiveIcon.getBackground(),
- adaptiveIcon.getForeground(), path),
- icon.getAppName()));
- } else if (drawable instanceof DynamicAdaptiveIconDrawable) {
- shapeIcons.add(icon);
- }
- // TODO: add iconloader library's legacy treatment helper methods for
- // non-adaptive icons
- }
- }
- return new PreviewInfo(context, mBodyFontFamily, mHeadlineFontFamily, mColorAccentLight,
- mColorAccentDark, mIcons, shapeDrawable, mCornerRadius, shapeIcons);
- }
-
- public Map<String, String> getPackages() {
- return Collections.unmodifiableMap(mPackages);
- }
-
- public String getTitle() {
- return mTitle;
- }
-
- public Builder setTitle(String title) {
- mTitle = title;
- return this;
- }
-
- public Builder setBodyFontFamily(@Nullable Typeface bodyFontFamily) {
- mBodyFontFamily = bodyFontFamily;
- return this;
- }
-
- public Builder setHeadlineFontFamily(@Nullable Typeface headlineFontFamily) {
- mHeadlineFontFamily = headlineFontFamily;
- return this;
- }
-
- public Builder setColorAccentLight(@ColorInt int colorAccentLight) {
- mColorAccentLight = colorAccentLight;
- return this;
- }
-
- public Builder setColorAccentDark(@ColorInt int colorAccentDark) {
- mColorAccentDark = colorAccentDark;
- return this;
- }
-
- public Builder addIcon(Drawable icon) {
- mIcons.add(icon);
- return this;
- }
-
- public Builder addOverlayPackage(String category, String packageName) {
- mPackages.put(category, packageName);
- return this;
- }
-
- public Builder setShapePath(String path) {
- mPathString = path;
- return this;
- }
-
- public Builder setShapePath(Path path) {
- mShapePath = path;
- return this;
- }
-
- public Builder asDefault() {
- mIsDefault = true;
- return this;
- }
-
- public Builder setShapePreviewIcons(List<ShapeAppIcon> appIcons) {
- mAppIcons.clear();
- mAppIcons.addAll(appIcons);
- return this;
- }
-
- public Builder setBottomSheetCornerRadius(@Dimension int radius) {
- mCornerRadius = radius;
- return this;
- }
- }
-}
diff --git a/src/com/android/customization/model/theme/ThemeBundleProvider.java b/src/com/android/customization/model/theme/ThemeBundleProvider.java
deleted file mode 100644
index 34342b31..00000000
--- a/src/com/android/customization/model/theme/ThemeBundleProvider.java
+++ /dev/null
@@ -1,51 +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.model.theme;
-
-import androidx.annotation.Nullable;
-
-import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
-import com.android.customization.model.theme.custom.CustomTheme;
-
-import org.json.JSONException;
-
-/**
- * Interface for a class that can retrieve Themes from the system.
- */
-public interface ThemeBundleProvider {
-
- /**
- * Returns whether themes are available in the current setup.
- */
- boolean isAvailable();
-
- /**
- * Retrieve the available themes.
- * @param callback called when the themes have been retrieved (or immediately if cached)
- * @param reload whether to reload themes if they're cached.
- */
- void fetch(OptionsFetchedListener<ThemeBundle> callback, boolean reload);
-
- void storeCustomTheme(CustomTheme theme);
-
- void removeCustomTheme(CustomTheme theme);
-
- @Nullable ThemeBundle.Builder parseThemeBundle(String serializedTheme) throws JSONException;
-
- @Nullable CustomTheme.Builder parseCustomTheme(String serializedTheme) throws JSONException;
-
- ThemeBundle findEquivalent(ThemeBundle other);
-}
diff --git a/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java b/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java
deleted file mode 100644
index 4f0cd6c7..00000000
--- a/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java
+++ /dev/null
@@ -1,190 +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.model.theme;
-
-import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.Parcel;
-import android.util.Log;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.StringRes;
-
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.ResourceAsset;
-import com.android.wallpaper.model.InlinePreviewIntentFactory;
-import com.android.wallpaper.model.WallpaperInfo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents a wallpaper coming from the resources of the theme bundle container APK.
- */
-public class ThemeBundledWallpaperInfo extends WallpaperInfo {
- public static final Creator<ThemeBundledWallpaperInfo> CREATOR =
- new Creator<ThemeBundledWallpaperInfo>() {
- @Override
- public ThemeBundledWallpaperInfo createFromParcel(Parcel in) {
- return new ThemeBundledWallpaperInfo(in);
- }
-
- @Override
- public ThemeBundledWallpaperInfo[] newArray(int size) {
- return new ThemeBundledWallpaperInfo[size];
- }
- };
-
- private static final String TAG = "ThemeBundledWallpaperInfo";
-
- private final String mPackageName;
- private final String mResName;
- private final String mCollectionId;
- @DrawableRes private final int mDrawableResId;
- @StringRes private final int mTitleResId;
- @StringRes private final int mAttributionResId;
- @StringRes private final int mActionUrlResId;
- private List<String> mAttributions;
- private String mActionUrl;
- private Resources mResources;
- private Asset mAsset;
-
- /**
- * Constructs a new theme-bundled static wallpaper model object.
- *
- * @param drawableResId Resource ID of the raw wallpaper image.
- * @param resName The unique name of the wallpaper resource, e.g. "z_wp001".
- * @param themeName Unique name of the collection this wallpaper belongs in; used for logging.
- * @param titleResId Resource ID of the string for the title attribution.
- * @param attributionResId Resource ID of the string for the first subtitle attribution.
- */
- public ThemeBundledWallpaperInfo(String packageName, String resName, String themeName,
- int drawableResId, int titleResId, int attributionResId, int actionUrlResId) {
- mPackageName = packageName;
- mResName = resName;
- mCollectionId = themeName;
- mDrawableResId = drawableResId;
- mTitleResId = titleResId;
- mAttributionResId = attributionResId;
- mActionUrlResId = actionUrlResId;
- }
-
- private ThemeBundledWallpaperInfo(Parcel in) {
- super(in);
- mPackageName = in.readString();
- mResName = in.readString();
- mCollectionId = in.readString();
- mDrawableResId = in.readInt();
- mTitleResId = in.readInt();
- mAttributionResId = in.readInt();
- mActionUrlResId = in.readInt();
- }
-
- @Override
- public List<String> getAttributions(Context context) {
- if (mAttributions == null) {
- Resources res = getPackageResources(context);
- mAttributions = new ArrayList<>();
- if (mTitleResId != 0) {
- mAttributions.add(res.getString(mTitleResId));
- }
- if (mAttributionResId != 0) {
- mAttributions.add(res.getString(mAttributionResId));
- }
- }
-
- return mAttributions;
- }
-
- @Override
- public String getActionUrl(Context context) {
- if (mActionUrl == null && mActionUrlResId != 0) {
- mActionUrl = getPackageResources(context).getString(mActionUrlResId);
- }
- return mActionUrl;
- }
-
- @Override
- public Asset getAsset(Context context) {
- if (mAsset == null) {
- Resources res = getPackageResources(context);
- mAsset = new ResourceAsset(res, mDrawableResId);
- }
-
- return mAsset;
- }
-
- @Override
- public Asset getThumbAsset(Context context) {
- return getAsset(context);
- }
-
- @Override
- public void showPreview(Activity srcActivity, InlinePreviewIntentFactory factory,
- int requestCode, boolean isAssetIdPresent) {
- try {
- 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
- public String getCollectionId(Context unused) {
- return mCollectionId;
- }
-
- @Override
- public String getWallpaperId() {
- return mResName;
- }
-
- public String getResName() {
- return mResName;
- }
-
- /**
- * Returns the {@link Resources} instance for the theme bundles stub APK.
- */
- private Resources getPackageResources(Context context) {
- if (mResources != null) {
- return mResources;
- }
-
- try {
- mResources = context.getPackageManager().getResourcesForApplication(mPackageName);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Could not get app resources for " + mPackageName);
- }
- return mResources;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeString(mPackageName);
- dest.writeString(mResName);
- dest.writeString(mCollectionId);
- dest.writeInt(mDrawableResId);
- dest.writeInt(mTitleResId);
- dest.writeInt(mAttributionResId);
- dest.writeInt(mActionUrlResId);
- }
-}
diff --git a/src/com/android/customization/model/theme/ThemeManager.java b/src/com/android/customization/model/theme/ThemeManager.java
deleted file mode 100644
index 85241c13..00000000
--- a/src/com/android/customization/model/theme/ThemeManager.java
+++ /dev/null
@@ -1,149 +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.model.theme;
-
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_ANDROID;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_LAUNCHER;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SETTINGS;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SYSUI;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_THEMEPICKER;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
-
-import android.provider.Settings;
-import android.text.TextUtils;
-
-import androidx.annotation.Nullable;
-import androidx.fragment.app.FragmentActivity;
-
-import com.android.customization.model.CustomizationManager;
-import com.android.customization.model.ResourceConstants;
-import com.android.customization.model.theme.custom.CustomTheme;
-import com.android.customization.module.ThemesUserEventLogger;
-
-import org.json.JSONObject;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class ThemeManager implements CustomizationManager<ThemeBundle> {
-
- public static final Set<String> THEME_CATEGORIES = new HashSet<>();
- static {
- THEME_CATEGORIES.add(OVERLAY_CATEGORY_COLOR);
- THEME_CATEGORIES.add(OVERLAY_CATEGORY_FONT);
- THEME_CATEGORIES.add(OVERLAY_CATEGORY_SHAPE);
- THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_ANDROID);
- THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_SETTINGS);
- THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_SYSUI);
- THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_LAUNCHER);
- THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_THEMEPICKER);
- }
-
- private final ThemeBundleProvider mProvider;
- private final OverlayManagerCompat mOverlayManagerCompat;
-
- protected final FragmentActivity mActivity;
- private final ThemesUserEventLogger mEventLogger;
-
- private Map<String, String> mCurrentOverlays;
-
- public ThemeManager(ThemeBundleProvider provider, FragmentActivity activity,
- OverlayManagerCompat overlayManagerCompat,
- ThemesUserEventLogger logger) {
- mProvider = provider;
- mActivity = activity;
- mOverlayManagerCompat = overlayManagerCompat;
- mEventLogger = logger;
- }
-
- @Override
- public boolean isAvailable() {
- return mOverlayManagerCompat.isAvailable() && mProvider.isAvailable();
- }
-
- @Override
- public void apply(ThemeBundle theme, Callback callback) {
- applyOverlays(theme, callback);
- }
-
- private void applyOverlays(ThemeBundle theme, Callback callback) {
- boolean allApplied = Settings.Secure.putString(mActivity.getContentResolver(),
- ResourceConstants.THEME_SETTING, theme.getSerializedPackagesWithTimestamp());
- if (theme instanceof CustomTheme) {
- storeCustomTheme((CustomTheme) theme);
- }
- mCurrentOverlays = null;
- if (allApplied) {
- mEventLogger.logThemeApplied(theme, theme instanceof CustomTheme);
- callback.onSuccess();
- } else {
- callback.onError(null);
- }
- }
-
- private void storeCustomTheme(CustomTheme theme) {
- mProvider.storeCustomTheme(theme);
- }
-
- @Override
- public void fetchOptions(OptionsFetchedListener<ThemeBundle> callback, boolean reload) {
- mProvider.fetch(callback, reload);
- }
-
- public Map<String, String> getCurrentOverlays() {
- if (mCurrentOverlays == null) {
- mCurrentOverlays = mOverlayManagerCompat.getEnabledOverlaysForTargets(
- ResourceConstants.getPackagesToOverlay(mActivity));
- mCurrentOverlays.entrySet().removeIf(
- categoryAndPackage -> !THEME_CATEGORIES.contains(categoryAndPackage.getKey()));
- }
- return mCurrentOverlays;
- }
-
- public String getStoredOverlays() {
- return Settings.Secure.getString(mActivity.getContentResolver(),
- ResourceConstants.THEME_SETTING);
- }
-
- public void removeCustomTheme(CustomTheme theme) {
- mProvider.removeCustomTheme(theme);
- }
-
- /**
- * @return an existing ThemeBundle that matches the same packages as the given one, if one
- * exists, or {@code null} otherwise.
- */
- @Nullable
- public ThemeBundle findThemeByPackages(ThemeBundle other) {
- return mProvider.findEquivalent(other);
- }
-
- /**
- * Store empty theme if no theme has been set yet. This will prevent Settings from showing the
- * suggestion to select a theme
- */
- public void storeEmptyTheme() {
- String themeSetting = Settings.Secure.getString(mActivity.getContentResolver(),
- ResourceConstants.THEME_SETTING);
- if (TextUtils.isEmpty(themeSetting)) {
- Settings.Secure.putString(mActivity.getContentResolver(),
- ResourceConstants.THEME_SETTING, new JSONObject().toString());
- }
- }
-}
diff --git a/src/com/android/customization/model/theme/custom/ColorOptionsProvider.java b/src/com/android/customization/model/theme/custom/ColorOptionsProvider.java
deleted file mode 100644
index f3b950b5..00000000
--- a/src/com/android/customization/model/theme/custom/ColorOptionsProvider.java
+++ /dev/null
@@ -1,171 +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.model.theme.custom;
-
-import static com.android.customization.model.ResourceConstants.ACCENT_COLOR_DARK_NAME;
-import static com.android.customization.model.ResourceConstants.ACCENT_COLOR_LIGHT_NAME;
-import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
-import static com.android.customization.model.ResourceConstants.ICONS_FOR_PREVIEW;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ANDROID_THEME;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_ANDROID;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
-import static com.android.customization.model.ResourceConstants.PATH_SIZE;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.PathShape;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.core.graphics.PathParser;
-
-import com.android.customization.model.ResourceConstants;
-import com.android.customization.model.theme.OverlayManagerCompat;
-import com.android.customization.model.theme.custom.ThemeComponentOption.ColorOption;
-import com.android.wallpaper.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Implementation of {@link ThemeComponentOptionProvider} that reads {@link ColorOption}s from
- * icon overlays.
- */
-public class ColorOptionsProvider extends ThemeComponentOptionProvider<ColorOption> {
-
- private static final String TAG = "ColorOptionsProvider";
- private final CustomThemeManager mCustomThemeManager;
- private final String mDefaultThemePackage;
-
- public ColorOptionsProvider(Context context, OverlayManagerCompat manager,
- CustomThemeManager customThemeManager) {
- super(context, manager, OVERLAY_CATEGORY_COLOR);
- mCustomThemeManager = customThemeManager;
- // System color is set with a static overlay for android.theme category, so let's try to
- // find that first, and if that's not present, we'll default to System resources.
- // (see #addDefault())
- List<String> themePackages = manager.getOverlayPackagesForCategory(
- OVERLAY_CATEGORY_ANDROID_THEME, UserHandle.myUserId(), ANDROID_PACKAGE);
- mDefaultThemePackage = themePackages.isEmpty() ? null : themePackages.get(0);
- }
-
- @Override
- protected void loadOptions() {
- List<Drawable> previewIcons = new ArrayList<>();
- String iconPackage =
- mCustomThemeManager.getOverlayPackages().get(OVERLAY_CATEGORY_ICON_ANDROID);
- if (TextUtils.isEmpty(iconPackage)) {
- iconPackage = ANDROID_PACKAGE;
- }
- for (String iconName : ICONS_FOR_PREVIEW) {
- try {
- previewIcons.add(loadIconPreviewDrawable(iconName, iconPackage));
- } catch (NameNotFoundException | NotFoundException e) {
- Log.w(TAG, String.format("Couldn't load icon in %s for color preview, will skip it",
- iconPackage), e);
- }
- }
- String shapePackage = mCustomThemeManager.getOverlayPackages().get(OVERLAY_CATEGORY_SHAPE);
- if (TextUtils.isEmpty(shapePackage)) {
- shapePackage = ANDROID_PACKAGE;
- }
- Drawable shape = loadShape(shapePackage);
- addDefault(previewIcons, shape);
- for (String overlayPackage : mOverlayPackages) {
- try {
- Resources overlayRes = getOverlayResources(overlayPackage);
- int lightColor = overlayRes.getColor(
- overlayRes.getIdentifier(ACCENT_COLOR_LIGHT_NAME, "color", overlayPackage),
- null);
- int darkColor = overlayRes.getColor(
- overlayRes.getIdentifier(ACCENT_COLOR_DARK_NAME, "color", overlayPackage),
- null);
- PackageManager pm = mContext.getPackageManager();
- String label = pm.getApplicationInfo(overlayPackage, 0).loadLabel(pm).toString();
- ColorOption option = new ColorOption(overlayPackage, label, lightColor, darkColor);
- option.setPreviewIcons(previewIcons);
- option.setShapeDrawable(shape);
- mOptions.add(option);
- } catch (NameNotFoundException | NotFoundException e) {
- Log.w(TAG, String.format("Couldn't load color overlay %s, will skip it",
- overlayPackage), e);
- }
- }
- }
-
- private void addDefault(List<Drawable> previewIcons, Drawable shape) {
- int lightColor, darkColor;
- Resources system = Resources.getSystem();
- try {
- Resources r = getOverlayResources(mDefaultThemePackage);
- lightColor = r.getColor(
- r.getIdentifier(ACCENT_COLOR_LIGHT_NAME, "color", mDefaultThemePackage),
- null);
- darkColor = r.getColor(
- r.getIdentifier(ACCENT_COLOR_DARK_NAME, "color", mDefaultThemePackage),
- null);
- } catch (NotFoundException | NameNotFoundException e) {
- Log.d(TAG, "Didn't find default color, will use system option", e);
-
- lightColor = system.getColor(
- system.getIdentifier(ACCENT_COLOR_LIGHT_NAME, "color", ANDROID_PACKAGE), null);
-
- darkColor = system.getColor(
- system.getIdentifier(ACCENT_COLOR_DARK_NAME, "color", ANDROID_PACKAGE), null);
- }
- ColorOption option = new ColorOption(null,
- mContext.getString(R.string.default_theme_title), lightColor, darkColor);
- option.setPreviewIcons(previewIcons);
- option.setShapeDrawable(shape);
- mOptions.add(option);
- }
-
- private Drawable loadIconPreviewDrawable(String drawableName, String packageName)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes = getOverlayResources(packageName);
- return overlayRes.getDrawable(
- overlayRes.getIdentifier(drawableName, "drawable", packageName), null);
- }
-
- private Drawable loadShape(String packageName) {
- String path = null;
- try {
- Resources r = getOverlayResources(packageName);
-
- path = ResourceConstants.getIconMask(r, packageName);
- } catch (NameNotFoundException e) {
- Log.d(TAG, String.format("Couldn't load shape icon for %s, skipping.", packageName), e);
- }
- ShapeDrawable shapeDrawable = null;
- if (!TextUtils.isEmpty(path)) {
- PathShape shape = new PathShape(PathParser.createPathFromPathData(path),
- PATH_SIZE, PATH_SIZE);
- shapeDrawable = new ShapeDrawable(shape);
- shapeDrawable.setIntrinsicHeight((int) PATH_SIZE);
- shapeDrawable.setIntrinsicWidth((int) PATH_SIZE);
- }
- return shapeDrawable;
- }
-
-}
diff --git a/src/com/android/customization/model/theme/custom/CustomTheme.java b/src/com/android/customization/model/theme/custom/CustomTheme.java
deleted file mode 100644
index a1ee1066..00000000
--- a/src/com/android/customization/model/theme/custom/CustomTheme.java
+++ /dev/null
@@ -1,106 +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.model.theme.custom;
-
-import android.content.Context;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.customization.model.CustomizationManager;
-import com.android.customization.model.theme.ThemeBundle;
-import com.android.wallpaper.R;
-
-import java.util.Map;
-import java.util.UUID;
-
-public class CustomTheme extends ThemeBundle {
-
- public static String newId() {
- return UUID.randomUUID().toString();
- }
-
- /**
- * Used to uniquely identify a custom theme since names can change.
- */
- private final String mId;
-
- private CustomTheme(@NonNull String id, String title, Map<String, String> overlayPackages,
- @Nullable PreviewInfo previewInfo) {
- super(title, overlayPackages, false, previewInfo);
- mId = id;
- }
-
- public String getId() {
- return mId;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof CustomTheme)) {
- return false;
- }
- CustomTheme other = (CustomTheme) obj;
- return mId.equals(other.mId);
- }
-
- @Override
- public int hashCode() {
- return mId.hashCode();
- }
-
- @Override
- public void bindThumbnailTile(View view) {
- if (isDefined()) {
- super.bindThumbnailTile(view);
- }
- }
-
- @Override
- public int getLayoutResId() {
- return isDefined() ? R.layout.theme_option : R.layout.custom_theme_option;
- }
-
- @Override
- public boolean isActive(CustomizationManager<ThemeBundle> manager) {
- return isDefined() && super.isActive(manager);
- }
-
- @Override
- public boolean isEquivalent(ThemeBundle other) {
- return isDefined() && super.isEquivalent(other);
- }
-
- public boolean isDefined() {
- return getPreviewInfo() != null;
- }
-
- public static class Builder extends ThemeBundle.Builder {
- private String mId;
-
- @Override
- public CustomTheme build(Context context) {
- return new CustomTheme(mId, mTitle, mPackages,
- mPackages.isEmpty() ? null : createPreviewInfo(context));
- }
-
- public Builder setId(String id) {
- mId = id;
- return this;
- }
- }
-}
diff --git a/src/com/android/customization/model/theme/custom/CustomThemeManager.java b/src/com/android/customization/model/theme/custom/CustomThemeManager.java
deleted file mode 100644
index 42d73e60..00000000
--- a/src/com/android/customization/model/theme/custom/CustomThemeManager.java
+++ /dev/null
@@ -1,116 +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.model.theme.custom;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-
-import com.android.customization.model.CustomizationManager;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
-import com.android.customization.model.theme.ThemeBundleProvider;
-import com.android.customization.model.theme.ThemeManager;
-import com.android.customization.model.theme.custom.CustomTheme.Builder;
-
-import org.json.JSONException;
-
-import java.util.Map;
-
-public class CustomThemeManager implements CustomizationManager<ThemeComponentOption> {
-
- private static final String TAG = "CustomThemeManager";
- private static final String KEY_STATE_CURRENT_SELECTION = "CustomThemeManager.currentSelection";
-
- private final CustomTheme mOriginalTheme;
- private CustomTheme.Builder mBuilder;
-
- private CustomThemeManager(Map<String, String> overlayPackages,
- @Nullable CustomTheme originalTheme) {
- mBuilder = new Builder();
- overlayPackages.forEach(mBuilder::addOverlayPackage);
- mOriginalTheme = originalTheme;
- }
-
- @Override
- public boolean isAvailable() {
- return true;
- }
-
- @Override
- public void apply(ThemeComponentOption option, @Nullable Callback callback) {
- option.buildStep(mBuilder);
- if (callback != null) {
- callback.onSuccess();
- }
- }
-
- public Map<String, String> getOverlayPackages() {
- return mBuilder.getPackages();
- }
-
- public CustomTheme buildPartialCustomTheme(Context context, String id, String title) {
- return ((CustomTheme.Builder)mBuilder.setId(id).setTitle(title)).build(context);
- }
-
- @Override
- public void fetchOptions(OptionsFetchedListener<ThemeComponentOption> callback, boolean reload) {
- //Unused
- }
-
- public CustomTheme getOriginalTheme() {
- return mOriginalTheme;
- }
-
- public PreviewInfo buildCustomThemePreviewInfo(Context context) {
- return mBuilder.createPreviewInfo(context);
- }
-
- /** Saves the custom theme selections while system config changes. */
- public void saveCustomTheme(Context context, Bundle savedInstanceState) {
- CustomTheme customTheme =
- buildPartialCustomTheme(context, /* id= */ null, /* title= */ null);
- savedInstanceState.putString(KEY_STATE_CURRENT_SELECTION,
- customTheme.getSerializedPackages());
- }
-
- /** Reads the saved custom theme after system config changed. */
- public void readCustomTheme(ThemeBundleProvider themeBundleProvider,
- Bundle savedInstanceState) {
- String packages = savedInstanceState.getString(KEY_STATE_CURRENT_SELECTION);
- if (!TextUtils.isEmpty(packages)) {
- try {
- mBuilder = themeBundleProvider.parseCustomTheme(packages);
- } catch (JSONException e) {
- Log.w(TAG, "Couldn't parse provided custom theme.");
- }
- } else {
- Log.w(TAG, "No custom theme being restored.");
- }
- }
-
- public static CustomThemeManager create(
- @Nullable CustomTheme customTheme, ThemeManager themeManager) {
- if (customTheme != null && customTheme.isDefined()) {
- return new CustomThemeManager(customTheme.getPackagesByCategory(), customTheme);
- }
- // Seed the first custom theme with the currently applied theme.
- return new CustomThemeManager(themeManager.getCurrentOverlays(), customTheme);
- }
-
-}
diff --git a/src/com/android/customization/model/theme/custom/FontOptionsProvider.java b/src/com/android/customization/model/theme/custom/FontOptionsProvider.java
deleted file mode 100644
index 53568c9f..00000000
--- a/src/com/android/customization/model/theme/custom/FontOptionsProvider.java
+++ /dev/null
@@ -1,86 +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.model.theme.custom;
-
-import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
-import static com.android.customization.model.ResourceConstants.CONFIG_BODY_FONT_FAMILY;
-import static com.android.customization.model.ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.Typeface;
-import android.util.Log;
-
-import com.android.customization.model.ResourceConstants;
-import com.android.customization.model.theme.OverlayManagerCompat;
-import com.android.customization.model.theme.custom.ThemeComponentOption.FontOption;
-import com.android.wallpaper.R;
-
-/**
- * Implementation of {@link ThemeComponentOptionProvider} that reads {@link FontOption}s from
- * font overlays.
- */
-public class FontOptionsProvider extends ThemeComponentOptionProvider<FontOption> {
-
- private static final String TAG = "FontOptionsProvider";
-
- public FontOptionsProvider(Context context, OverlayManagerCompat manager) {
- super(context, manager, OVERLAY_CATEGORY_FONT);
- }
-
- @Override
- protected void loadOptions() {
- addDefault();
- for (String overlayPackage : mOverlayPackages) {
- try {
- Resources overlayRes = getOverlayResources(overlayPackage);
- Typeface headlineFont = Typeface.create(
- getFontFamily(overlayPackage, overlayRes, CONFIG_HEADLINE_FONT_FAMILY),
- Typeface.NORMAL);
- Typeface bodyFont = Typeface.create(
- getFontFamily(overlayPackage, overlayRes, CONFIG_BODY_FONT_FAMILY),
- Typeface.NORMAL);
- PackageManager pm = mContext.getPackageManager();
- String label = pm.getApplicationInfo(overlayPackage, 0).loadLabel(pm).toString();
- mOptions.add(new FontOption(overlayPackage, label, headlineFont, bodyFont));
- } catch (NameNotFoundException | NotFoundException e) {
- Log.w(TAG, String.format("Couldn't load font overlay %s, will skip it",
- overlayPackage), e);
- }
- }
- }
-
- private void addDefault() {
- Resources system = Resources.getSystem();
- Typeface headlineFont = Typeface.create(system.getString(system.getIdentifier(
- ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY,"string", ANDROID_PACKAGE)),
- Typeface.NORMAL);
- Typeface bodyFont = Typeface.create(system.getString(system.getIdentifier(
- ResourceConstants.CONFIG_BODY_FONT_FAMILY,
- "string", ANDROID_PACKAGE)),
- Typeface.NORMAL);
- mOptions.add(new FontOption(null, mContext.getString(R.string.default_theme_title),
- headlineFont, bodyFont));
- }
-
- private String getFontFamily(String overlayPackage, Resources overlayRes, String configName) {
- return overlayRes.getString(overlayRes.getIdentifier(configName, "string", overlayPackage));
- }
-}
diff --git a/src/com/android/customization/model/theme/custom/IconOptionsProvider.java b/src/com/android/customization/model/theme/custom/IconOptionsProvider.java
deleted file mode 100644
index f7b669b1..00000000
--- a/src/com/android/customization/model/theme/custom/IconOptionsProvider.java
+++ /dev/null
@@ -1,153 +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.model.theme.custom;
-
-import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
-import static com.android.customization.model.ResourceConstants.ICONS_FOR_PREVIEW;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_ANDROID;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_LAUNCHER;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SETTINGS;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SYSUI;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_THEMEPICKER;
-
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.customization.model.ResourceConstants;
-import com.android.customization.model.theme.OverlayManagerCompat;
-import com.android.customization.model.theme.custom.ThemeComponentOption.IconOption;
-import com.android.wallpaper.R;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Implementation of {@link ThemeComponentOptionProvider} that reads {@link IconOption}s from
- * icon overlays.
- */
-public class IconOptionsProvider extends ThemeComponentOptionProvider<IconOption> {
-
- private static final String TAG = "IconOptionsProvider";
-
- private final List<String> mSysUiIconsOverlayPackages = new ArrayList<>();
- private final List<String> mSettingsIconsOverlayPackages = new ArrayList<>();
- private final List<String> mLauncherIconsOverlayPackages = new ArrayList<>();
- private final List<String> mThemePickerIconsOverlayPackages = new ArrayList<>();
-
- public IconOptionsProvider(Context context, OverlayManagerCompat manager) {
- super(context, manager, OVERLAY_CATEGORY_ICON_ANDROID);
- String[] targetPackages = ResourceConstants.getPackagesToOverlay(context);
- mSysUiIconsOverlayPackages.addAll(manager.getOverlayPackagesForCategory(
- OVERLAY_CATEGORY_ICON_SYSUI, UserHandle.myUserId(), targetPackages));
- mSettingsIconsOverlayPackages.addAll(manager.getOverlayPackagesForCategory(
- OVERLAY_CATEGORY_ICON_SETTINGS, UserHandle.myUserId(), targetPackages));
- mLauncherIconsOverlayPackages.addAll(manager.getOverlayPackagesForCategory(
- OVERLAY_CATEGORY_ICON_LAUNCHER, UserHandle.myUserId(), targetPackages));
- mThemePickerIconsOverlayPackages.addAll(manager.getOverlayPackagesForCategory(
- OVERLAY_CATEGORY_ICON_THEMEPICKER, UserHandle.myUserId(), targetPackages));
- }
-
- @Override
- protected void loadOptions() {
- addDefault();
-
- Map<String, IconOption> optionsByPrefix = new HashMap<>();
- for (String overlayPackage : mOverlayPackages) {
- IconOption option = addOrUpdateOption(optionsByPrefix, overlayPackage,
- OVERLAY_CATEGORY_ICON_ANDROID);
- try{
- for (String iconName : ICONS_FOR_PREVIEW) {
- option.addIcon(loadIconPreviewDrawable(iconName, overlayPackage));
- }
- } catch (NotFoundException | NameNotFoundException e) {
- Log.w(TAG, String.format("Couldn't load icon overlay details for %s, will skip it",
- overlayPackage), e);
- }
- }
-
- for (String overlayPackage : mSysUiIconsOverlayPackages) {
- addOrUpdateOption(optionsByPrefix, overlayPackage, OVERLAY_CATEGORY_ICON_SYSUI);
- }
-
- for (String overlayPackage : mSettingsIconsOverlayPackages) {
- addOrUpdateOption(optionsByPrefix, overlayPackage, OVERLAY_CATEGORY_ICON_SETTINGS);
- }
-
- for (String overlayPackage : mLauncherIconsOverlayPackages) {
- addOrUpdateOption(optionsByPrefix, overlayPackage, OVERLAY_CATEGORY_ICON_LAUNCHER);
- }
-
- for (String overlayPackage : mThemePickerIconsOverlayPackages) {
- addOrUpdateOption(optionsByPrefix, overlayPackage, OVERLAY_CATEGORY_ICON_THEMEPICKER);
- }
-
- for (IconOption option : optionsByPrefix.values()) {
- if (option.isValid(mContext)) {
- mOptions.add(option);
- option.setLabel(mContext.getString(R.string.icon_component_label, mOptions.size()));
- }
- }
- }
-
- private IconOption addOrUpdateOption(Map<String, IconOption> optionsByPrefix,
- String overlayPackage, String category) {
- String prefix = overlayPackage.substring(0, overlayPackage.lastIndexOf("."));
- IconOption option;
- if (!optionsByPrefix.containsKey(prefix)) {
- option = new IconOption();
- optionsByPrefix.put(prefix, option);
- } else {
- option = optionsByPrefix.get(prefix);
- }
- option.addOverlayPackage(category, overlayPackage);
- return option;
- }
-
- private Drawable loadIconPreviewDrawable(String drawableName, String packageName)
- throws NameNotFoundException, NotFoundException {
- final Resources resources = ANDROID_PACKAGE.equals(packageName)
- ? Resources.getSystem()
- : mContext.getPackageManager().getResourcesForApplication(packageName);
- return resources.getDrawable(
- resources.getIdentifier(drawableName, "drawable", packageName), null);
- }
-
- private void addDefault() {
- IconOption option = new IconOption();
- option.setLabel(mContext.getString(R.string.default_theme_title));
- try {
- for (String iconName : ICONS_FOR_PREVIEW) {
- option.addIcon(loadIconPreviewDrawable(iconName, ANDROID_PACKAGE));
- }
- } catch (NameNotFoundException | NotFoundException e) {
- Log.w(TAG, "Didn't find SystemUi package icons, will skip option", e);
- }
- option.addOverlayPackage(OVERLAY_CATEGORY_ICON_ANDROID, null);
- option.addOverlayPackage(OVERLAY_CATEGORY_ICON_SYSUI, null);
- option.addOverlayPackage(OVERLAY_CATEGORY_ICON_SETTINGS, null);
- option.addOverlayPackage(OVERLAY_CATEGORY_ICON_LAUNCHER, null);
- option.addOverlayPackage(OVERLAY_CATEGORY_ICON_THEMEPICKER, null);
- mOptions.add(option);
- }
-
-}
diff --git a/src/com/android/customization/model/theme/custom/ShapeOptionsProvider.java b/src/com/android/customization/model/theme/custom/ShapeOptionsProvider.java
deleted file mode 100644
index f93b8920..00000000
--- a/src/com/android/customization/model/theme/custom/ShapeOptionsProvider.java
+++ /dev/null
@@ -1,154 +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.model.theme.custom;
-
-import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
-import static com.android.customization.model.ResourceConstants.CONFIG_CORNERRADIUS;
-import static com.android.customization.model.ResourceConstants.CONFIG_ICON_MASK;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
-import static com.android.customization.model.ResourceConstants.PATH_SIZE;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.PathShape;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.Dimension;
-import androidx.core.graphics.PathParser;
-
-import com.android.customization.model.ResourceConstants;
-import com.android.customization.model.theme.OverlayManagerCompat;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo.ShapeAppIcon;
-import com.android.customization.model.theme.custom.ThemeComponentOption.ShapeOption;
-import com.android.customization.widget.DynamicAdaptiveIconDrawable;
-import com.android.wallpaper.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Implementation of {@link ThemeComponentOptionProvider} that reads {@link ShapeOption}s from
- * icon overlays.
- */
-public class ShapeOptionsProvider extends ThemeComponentOptionProvider<ShapeOption> {
-
- private static final String TAG = "ShapeOptionsProvider";
- private final String[] mShapePreviewIconPackages;
- private int mThumbSize;
-
- public ShapeOptionsProvider(Context context, OverlayManagerCompat manager) {
- super(context, manager, OVERLAY_CATEGORY_SHAPE);
- mShapePreviewIconPackages = context.getResources().getStringArray(
- R.array.icon_shape_preview_packages);
- mThumbSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.component_shape_thumb_size);
- }
-
- @Override
- protected void loadOptions() {
- addDefault();
- for (String overlayPackage : mOverlayPackages) {
- try {
- Path path = loadPath(mContext.getPackageManager()
- .getResourcesForApplication(overlayPackage), overlayPackage);
- PackageManager pm = mContext.getPackageManager();
- String label = pm.getApplicationInfo(overlayPackage, 0).loadLabel(pm).toString();
- mOptions.add(new ShapeOption(overlayPackage, label, path,
- loadCornerRadius(overlayPackage), createShapeDrawable(path),
- getShapedAppIcons(path)));
- } catch (NameNotFoundException | NotFoundException e) {
- Log.w(TAG, String.format("Couldn't load shape overlay %s, will skip it",
- overlayPackage), e);
- }
- }
- }
-
- private void addDefault() {
- Resources system = Resources.getSystem();
- Path path = loadPath(system, ANDROID_PACKAGE);
- mOptions.add(new ShapeOption(null, mContext.getString(R.string.default_theme_title), path,
- system.getDimensionPixelOffset(
- system.getIdentifier(ResourceConstants.CONFIG_CORNERRADIUS,
- "dimen", ResourceConstants.ANDROID_PACKAGE)),
- createShapeDrawable(path), getShapedAppIcons(path)));
- }
-
- private ShapeDrawable createShapeDrawable(Path path) {
- PathShape shape = new PathShape(path, PATH_SIZE, PATH_SIZE);
- ShapeDrawable shapeDrawable = new ShapeDrawable(shape);
- shapeDrawable.setIntrinsicHeight(mThumbSize);
- shapeDrawable.setIntrinsicWidth(mThumbSize);
- return shapeDrawable;
- }
-
- private List<ShapeAppIcon> getShapedAppIcons(Path path) {
- List<ShapeAppIcon> shapedAppIcons = new ArrayList<>();
- for (String packageName : mShapePreviewIconPackages) {
- Drawable icon = null;
- CharSequence name = null;
- try {
- Drawable appIcon = mContext.getPackageManager().getApplicationIcon(packageName);
- if (appIcon instanceof AdaptiveIconDrawable) {
- AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) appIcon;
- icon = new DynamicAdaptiveIconDrawable(adaptiveIcon.getBackground(),
- adaptiveIcon.getForeground(), path);
-
- ApplicationInfo appInfo = mContext.getPackageManager()
- .getApplicationInfo(packageName, /* flag= */ 0);
- name = mContext.getPackageManager().getApplicationLabel(appInfo);
- }
- } catch (NameNotFoundException e) {
- Log.d(TAG, "Couldn't find app " + packageName
- + ", won't use it for icon shape preview");
- } finally {
- if (icon != null && !TextUtils.isEmpty(name)) {
- shapedAppIcons.add(new ShapeAppIcon(icon, name));
- }
- }
- }
- return shapedAppIcons;
- }
-
- private Path loadPath(Resources overlayRes, String packageName) {
- String shape = overlayRes.getString(overlayRes.getIdentifier(CONFIG_ICON_MASK, "string",
- packageName));
-
- if (!TextUtils.isEmpty(shape)) {
- return PathParser.createPathFromPathData(shape);
- }
- return null;
- }
-
- @Dimension
- private int loadCornerRadius(String packageName)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes =
- mContext.getPackageManager().getResourcesForApplication(
- packageName);
- return overlayRes.getDimensionPixelOffset(overlayRes.getIdentifier(
- CONFIG_CORNERRADIUS, "dimen", packageName));
- }
-}
diff --git a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
deleted file mode 100644
index 78be0fc0..00000000
--- a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
+++ /dev/null
@@ -1,556 +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.model.theme.custom;
-
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_ANDROID;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_LAUNCHER;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SETTINGS;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SYSUI;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_THEMEPICKER;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
-import static com.android.customization.model.ResourceConstants.getLauncherPackage;
-
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.content.res.Resources.Theme;
-import android.content.res.TypedArray;
-import android.graphics.Path;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.text.TextUtils;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-import android.widget.SeekBar;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.Dimension;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.core.graphics.ColorUtils;
-
-import com.android.customization.model.CustomizationManager;
-import com.android.customization.model.CustomizationOption;
-import com.android.customization.model.ResourceConstants;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo.ShapeAppIcon;
-import com.android.customization.model.theme.custom.CustomTheme.Builder;
-import com.android.wallpaper.R;
-import com.android.wallpaper.util.ResourceUtils;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Represents an option of a component of a custom Theme (for example, a possible color, or font,
- * shape, etc).
- * Extending classes correspond to each component's options and provide the structure to bind
- * preview and thumbnails.
- * // TODO (santie): refactor the logic to bind preview cards to reuse between ThemeFragment and
- * // here
- */
-public abstract class ThemeComponentOption implements CustomizationOption<ThemeComponentOption> {
-
- protected final Map<String, String> mOverlayPackageNames = new HashMap<>();
-
- protected void addOverlayPackage(String category, String packageName) {
- mOverlayPackageNames.put(category, packageName);
- }
-
- public Map<String, String> getOverlayPackages() {
- return mOverlayPackageNames;
- }
-
- @Override
- public String getTitle() {
- return null;
- }
-
- public abstract void bindPreview(ViewGroup container);
-
- public Builder buildStep(Builder builder) {
- getOverlayPackages().forEach(builder::addOverlayPackage);
- return builder;
- }
-
- public static class FontOption extends ThemeComponentOption {
-
- private final String mLabel;
- private final Typeface mHeadlineFont;
- private final Typeface mBodyFont;
-
- public FontOption(String packageName, String label, Typeface headlineFont,
- Typeface bodyFont) {
- addOverlayPackage(OVERLAY_CATEGORY_FONT, packageName);
- mLabel = label;
- mHeadlineFont = headlineFont;
- mBodyFont = bodyFont;
- }
-
- @Override
- public String getTitle() {
- return null;
- }
-
- @Override
- public void bindThumbnailTile(View view) {
- ((TextView) view.findViewById(R.id.thumbnail_text)).setTypeface(
- mHeadlineFont);
- view.setContentDescription(mLabel);
- }
-
- @Override
- public boolean isActive(CustomizationManager<ThemeComponentOption> manager) {
- CustomThemeManager customThemeManager = (CustomThemeManager) manager;
- return Objects.equals(getOverlayPackages().get(OVERLAY_CATEGORY_FONT),
- customThemeManager.getOverlayPackages().get(OVERLAY_CATEGORY_FONT));
- }
-
- @Override
- public int getLayoutResId() {
- return R.layout.theme_font_option;
- }
-
- @Override
- public void bindPreview(ViewGroup container) {
- container.setContentDescription(
- container.getContext().getString(R.string.font_preview_content_description));
-
- bindPreviewHeader(container, R.string.preview_name_font, R.drawable.ic_font, null);
-
- ViewGroup cardBody = container.findViewById(R.id.theme_preview_card_body_container);
- if (cardBody.getChildCount() == 0) {
- LayoutInflater.from(container.getContext()).inflate(
- R.layout.preview_card_font_content,
- cardBody, true);
- }
- TextView title = container.findViewById(R.id.font_card_title);
- title.setTypeface(mHeadlineFont);
- TextView bodyText = container.findViewById(R.id.font_card_body);
- bodyText.setTypeface(mBodyFont);
- container.findViewById(R.id.font_card_divider).setBackgroundColor(
- title.getCurrentTextColor());
- }
-
- @Override
- public Builder buildStep(Builder builder) {
- builder.setHeadlineFontFamily(mHeadlineFont).setBodyFontFamily(mBodyFont);
- return super.buildStep(builder);
- }
- }
-
- void bindPreviewHeader(ViewGroup container, @StringRes int headerTextResId,
- @DrawableRes int headerIcon, String drawableName) {
- TextView header = container.findViewById(R.id.theme_preview_card_header);
- header.setText(headerTextResId);
-
- Context context = container.getContext();
- Drawable icon;
- if (!TextUtils.isEmpty(drawableName)) {
- try {
- Resources resources = context.getPackageManager()
- .getResourcesForApplication(getLauncherPackage(context));
- icon = resources.getDrawable(resources.getIdentifier(
- drawableName, "drawable", getLauncherPackage(context)), null);
- } catch (NameNotFoundException | NotFoundException e) {
- icon = context.getResources().getDrawable(headerIcon, context.getTheme());
- }
- } else {
- icon = context.getResources().getDrawable(headerIcon, context.getTheme());
- }
- int size = context.getResources().getDimensionPixelSize(R.dimen.card_header_icon_size);
- icon.setBounds(0, 0, size, size);
-
- header.setCompoundDrawables(null, icon, null, null);
- header.setCompoundDrawableTintList(ColorStateList.valueOf(
- header.getCurrentTextColor()));
- }
-
- public static class IconOption extends ThemeComponentOption {
-
- public static final int THUMBNAIL_ICON_POSITION = 0;
- private static int[] mIconIds = {
- R.id.preview_icon_0, R.id.preview_icon_1, R.id.preview_icon_2, R.id.preview_icon_3,
- R.id.preview_icon_4, R.id.preview_icon_5
- };
-
- private List<Drawable> mIcons = new ArrayList<>();
- private String mLabel;
-
- @Override
- public void bindThumbnailTile(View view) {
- Resources res = view.getContext().getResources();
- Drawable icon = mIcons.get(THUMBNAIL_ICON_POSITION)
- .getConstantState().newDrawable().mutate();
- icon.setTint(ResourceUtils.getColorAttr(
- view.getContext(), android.R.attr.textColorSecondary));
- ((ImageView) view.findViewById(R.id.option_icon)).setImageDrawable(
- icon);
- view.setContentDescription(mLabel);
- }
-
- @Override
- public boolean isActive(CustomizationManager<ThemeComponentOption> manager) {
- CustomThemeManager customThemeManager = (CustomThemeManager) manager;
- Map<String, String> themePackages = customThemeManager.getOverlayPackages();
- if (getOverlayPackages().isEmpty()) {
- return themePackages.get(OVERLAY_CATEGORY_ICON_SYSUI) == null &&
- themePackages.get(OVERLAY_CATEGORY_ICON_SETTINGS) == null &&
- themePackages.get(OVERLAY_CATEGORY_ICON_ANDROID) == null &&
- themePackages.get(OVERLAY_CATEGORY_ICON_LAUNCHER) == null &&
- themePackages.get(OVERLAY_CATEGORY_ICON_THEMEPICKER) == null;
- }
- for (Map.Entry<String, String> overlayEntry : getOverlayPackages().entrySet()) {
- if(!Objects.equals(overlayEntry.getValue(),
- themePackages.get(overlayEntry.getKey()))) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public int getLayoutResId() {
- return R.layout.theme_icon_option;
- }
-
- @Override
- public void bindPreview(ViewGroup container) {
- container.setContentDescription(
- container.getContext().getString(R.string.icon_preview_content_description));
-
- bindPreviewHeader(container, R.string.preview_name_icon, R.drawable.ic_widget,
- "ic_widget");
-
- ViewGroup cardBody = container.findViewById(R.id.theme_preview_card_body_container);
- if (cardBody.getChildCount() == 0) {
- LayoutInflater.from(container.getContext()).inflate(
- R.layout.preview_card_icon_content, cardBody, true);
- }
- for (int i = 0; i < mIconIds.length && i < mIcons.size(); i++) {
- ((ImageView) container.findViewById(mIconIds[i])).setImageDrawable(
- mIcons.get(i));
- }
- }
-
- public void addIcon(Drawable previewIcon) {
- mIcons.add(previewIcon);
- }
-
- /**
- * @return whether this icon option has overlays and previews for all the required packages
- */
- public boolean isValid(Context context) {
- return getOverlayPackages().keySet().size() ==
- ResourceConstants.getPackagesToOverlay(context).length;
- }
-
- public void setLabel(String label) {
- mLabel = label;
- }
-
- @Override
- public Builder buildStep(Builder builder) {
- for (Drawable icon : mIcons) {
- builder.addIcon(icon);
- }
- return super.buildStep(builder);
- }
- }
-
- public static class ColorOption extends ThemeComponentOption {
-
- /**
- * Ids of views used to represent quick setting tiles in the color preview screen
- */
- private static int[] COLOR_TILE_IDS = {
- R.id.preview_color_qs_0_bg, R.id.preview_color_qs_1_bg, R.id.preview_color_qs_2_bg
- };
-
- /**
- * Ids of the views for the foreground of the icon, mapping to the corresponding index of
- * the actual icon drawable.
- */
- static int[][] COLOR_TILES_ICON_IDS = {
- new int[]{ R.id.preview_color_qs_0_icon, 0},
- new int[]{ R.id.preview_color_qs_1_icon, 1},
- new int[] { R.id.preview_color_qs_2_icon, 3}
- };
-
- /**
- * Ids of views used to represent control buttons in the color preview screen
- */
- private static int[] COLOR_BUTTON_IDS = {
- R.id.preview_check_selected, R.id.preview_radio_selected,
- R.id.preview_toggle_selected
- };
-
- @ColorInt private int mColorAccentLight;
- @ColorInt private int mColorAccentDark;
- /**
- * Icons shown as example of QuickSettings tiles in the color preview screen.
- */
- private List<Drawable> mIcons = new ArrayList<>();
-
- /**
- * Drawable with the currently selected shape to be used as background of the sample
- * QuickSetting icons in the color preview screen.
- */
- private Drawable mShapeDrawable;
-
- private String mLabel;
-
- ColorOption(String packageName, String label, @ColorInt int lightColor,
- @ColorInt int darkColor) {
- addOverlayPackage(OVERLAY_CATEGORY_COLOR, packageName);
- mLabel = label;
- mColorAccentLight = lightColor;
- mColorAccentDark = darkColor;
- }
-
- @Override
- public void bindThumbnailTile(View view) {
- @ColorInt int color = resolveColor(view.getResources());
- LayerDrawable selectedOption = (LayerDrawable) view.getResources().getDrawable(
- R.drawable.color_chip_hollow, view.getContext().getTheme());
- Drawable unselectedOption = view.getResources().getDrawable(
- R.drawable.color_chip_filled, view.getContext().getTheme());
-
- selectedOption.findDrawableByLayerId(R.id.center_fill).setTintList(
- ColorStateList.valueOf(color));
- unselectedOption.setTintList(ColorStateList.valueOf(color));
-
- StateListDrawable stateListDrawable = new StateListDrawable();
- stateListDrawable.addState(new int[] {android.R.attr.state_activated}, selectedOption);
- stateListDrawable.addState(
- new int[] {-android.R.attr.state_activated}, unselectedOption);
-
- ((ImageView) view.findViewById(R.id.option_tile)).setImageDrawable(stateListDrawable);
- view.setContentDescription(mLabel);
- }
-
- @ColorInt
- private int resolveColor(Resources res) {
- Configuration configuration = res.getConfiguration();
- return (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES ? mColorAccentDark : mColorAccentLight;
- }
-
- @Override
- public boolean isActive(CustomizationManager<ThemeComponentOption> manager) {
- CustomThemeManager customThemeManager = (CustomThemeManager) manager;
- return Objects.equals(getOverlayPackages().get(OVERLAY_CATEGORY_COLOR),
- customThemeManager.getOverlayPackages().get(OVERLAY_CATEGORY_COLOR));
- }
-
- @Override
- public int getLayoutResId() {
- return R.layout.theme_color_option;
- }
-
- @Override
- public void bindPreview(ViewGroup container) {
- container.setContentDescription(
- container.getContext().getString(R.string.color_preview_content_description));
-
- bindPreviewHeader(container, R.string.preview_name_color, R.drawable.ic_colorize_24px,
- null);
-
- ViewGroup cardBody = container.findViewById(R.id.theme_preview_card_body_container);
- if (cardBody.getChildCount() == 0) {
- LayoutInflater.from(container.getContext()).inflate(
- R.layout.preview_card_color_content, cardBody, true);
- }
- Resources res = container.getResources();
- @ColorInt int accentColor = resolveColor(res);
- @ColorInt int controlGreyColor = ResourceUtils.getColorAttr(
- container.getContext(),
- android.R.attr.textColorTertiary);
- ColorStateList tintList = new ColorStateList(
- new int[][]{
- new int[]{android.R.attr.state_selected},
- new int[]{android.R.attr.state_checked},
- new int[]{-android.R.attr.state_enabled}
- },
- new int[] {
- accentColor,
- accentColor,
- controlGreyColor
- }
- );
-
- for (int i = 0; i < COLOR_BUTTON_IDS.length; i++) {
- CompoundButton button = container.findViewById(COLOR_BUTTON_IDS[i]);
- button.setButtonTintList(tintList);
- }
-
- Switch enabledSwitch = container.findViewById(R.id.preview_toggle_selected);
- enabledSwitch.setThumbTintList(tintList);
- enabledSwitch.setTrackTintList(tintList);
-
- ColorStateList seekbarTintList = ColorStateList.valueOf(accentColor);
- SeekBar seekbar = container.findViewById(R.id.preview_seekbar);
- seekbar.setThumbTintList(seekbarTintList);
- seekbar.setProgressTintList(seekbarTintList);
- seekbar.setProgressBackgroundTintList(seekbarTintList);
- // Disable seekbar
- seekbar.setOnTouchListener((view, motionEvent) -> true);
-
- int iconFgColor = ResourceUtils.getColorAttr(container.getContext(),
- android.R.attr.colorBackground);
- if (!mIcons.isEmpty() && mShapeDrawable != null) {
- for (int i = 0; i < COLOR_TILE_IDS.length; i++) {
- Drawable icon = mIcons.get(COLOR_TILES_ICON_IDS[i][1]).getConstantState()
- .newDrawable();
- icon.setTint(iconFgColor);
- //TODO: load and set the shape.
- Drawable bgShape = mShapeDrawable.getConstantState().newDrawable();
- bgShape.setTint(accentColor);
-
- ImageView bg = container.findViewById(COLOR_TILE_IDS[i]);
- bg.setImageDrawable(bgShape);
- ImageView fg = container.findViewById(COLOR_TILES_ICON_IDS[i][0]);
- fg.setImageDrawable(icon);
- }
- }
- }
-
- public void setPreviewIcons(List<Drawable> icons) {
- mIcons.addAll(icons);
- }
-
- public void setShapeDrawable(@Nullable Drawable shapeDrawable) {
- mShapeDrawable = shapeDrawable;
- }
-
- @Override
- public Builder buildStep(Builder builder) {
- builder.setColorAccentDark(mColorAccentDark).setColorAccentLight(mColorAccentLight);
- return super.buildStep(builder);
- }
- }
-
- public static class ShapeOption extends ThemeComponentOption {
-
- private final LayerDrawable mShape;
- private final List<ShapeAppIcon> mAppIcons;
- private final String mLabel;
- private final Path mPath;
- private final int mCornerRadius;
- private int[] mShapeIconIds = {
- R.id.shape_preview_icon_0, R.id.shape_preview_icon_1, R.id.shape_preview_icon_2,
- R.id.shape_preview_icon_3, R.id.shape_preview_icon_4, R.id.shape_preview_icon_5
- };
-
- ShapeOption(String packageName, String label, Path path,
- @Dimension int cornerRadius, Drawable shapeDrawable,
- List<ShapeAppIcon> appIcons) {
- addOverlayPackage(OVERLAY_CATEGORY_SHAPE, packageName);
- mLabel = label;
- mAppIcons = appIcons;
- mPath = path;
- mCornerRadius = cornerRadius;
- Drawable background = shapeDrawable.getConstantState().newDrawable();
- Drawable foreground = shapeDrawable.getConstantState().newDrawable();
- mShape = new LayerDrawable(new Drawable[]{background, foreground});
- mShape.setLayerGravity(0, Gravity.CENTER);
- mShape.setLayerGravity(1, Gravity.CENTER);
- }
-
- @Override
- public void bindThumbnailTile(View view) {
- ImageView thumb = view.findViewById(R.id.shape_thumbnail);
- Resources res = view.getResources();
- Theme theme = view.getContext().getTheme();
- int borderWidth = 2 * res.getDimensionPixelSize(R.dimen.option_border_width);
-
- Drawable background = mShape.getDrawable(0);
- background.setTintList(res.getColorStateList(R.color.option_border_color, theme));
-
- ShapeDrawable foreground = (ShapeDrawable) mShape.getDrawable(1);
-
- foreground.setIntrinsicHeight(background.getIntrinsicHeight() - borderWidth);
- foreground.setIntrinsicWidth(background.getIntrinsicWidth() - borderWidth);
- TypedArray ta = view.getContext().obtainStyledAttributes(
- new int[]{android.R.attr.colorPrimary});
- int primaryColor = ta.getColor(0, 0);
- ta.recycle();
- int foregroundColor =
- ResourceUtils.getColorAttr(view.getContext(), android.R.attr.textColorPrimary);
-
- foreground.setTint(ColorUtils.blendARGB(primaryColor, foregroundColor, .05f));
-
- thumb.setImageDrawable(mShape);
- view.setContentDescription(mLabel);
- }
-
- @Override
- public boolean isActive(CustomizationManager<ThemeComponentOption> manager) {
- CustomThemeManager customThemeManager = (CustomThemeManager) manager;
- return Objects.equals(getOverlayPackages().get(OVERLAY_CATEGORY_SHAPE),
- customThemeManager.getOverlayPackages().get(OVERLAY_CATEGORY_SHAPE));
- }
-
- @Override
- public int getLayoutResId() {
- return R.layout.theme_shape_option;
- }
-
- @Override
- public void bindPreview(ViewGroup container) {
- container.setContentDescription(
- container.getContext().getString(R.string.shape_preview_content_description));
-
- bindPreviewHeader(container, R.string.preview_name_shape, R.drawable.ic_shapes_24px,
- null);
-
- ViewGroup cardBody = container.findViewById(R.id.theme_preview_card_body_container);
- if (cardBody.getChildCount() == 0) {
- LayoutInflater.from(container.getContext()).inflate(
- R.layout.preview_card_shape_content, cardBody, true);
- }
- for (int i = 0; i < mShapeIconIds.length && i < mAppIcons.size(); i++) {
- ImageView iconView = cardBody.findViewById(mShapeIconIds[i]);
- iconView.setBackground(mAppIcons.get(i).getDrawableCopy());
- }
- }
-
- @Override
- public Builder buildStep(Builder builder) {
- builder.setShapePath(mPath)
- .setBottomSheetCornerRadius(mCornerRadius)
- .setShapePreviewIcons(mAppIcons);
- return super.buildStep(builder);
- }
- }
-}
diff --git a/src/com/android/customization/model/theme/custom/ThemeComponentOptionProvider.java b/src/com/android/customization/model/theme/custom/ThemeComponentOptionProvider.java
deleted file mode 100644
index 992c47cc..00000000
--- a/src/com/android/customization/model/theme/custom/ThemeComponentOptionProvider.java
+++ /dev/null
@@ -1,78 +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.model.theme.custom;
-
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.os.UserHandle;
-
-import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
-import com.android.customization.model.ResourceConstants;
-import com.android.customization.model.theme.OverlayManagerCompat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Base class used to retrieve Custom Theme Component options (eg, different fonts)
- * from the system.
- */
-public abstract class ThemeComponentOptionProvider<T extends ThemeComponentOption> {
-
- protected final Context mContext;
- protected final List<String> mOverlayPackages;
- protected List<T> mOptions;
-
- public ThemeComponentOptionProvider(Context context, OverlayManagerCompat manager,
- String... categories) {
- mContext = context;
- mOverlayPackages = new ArrayList<>();
- for (String category : categories) {
- mOverlayPackages.addAll(manager.getOverlayPackagesForCategory(category,
- UserHandle.myUserId(), ResourceConstants.getPackagesToOverlay(mContext)));
- }
- }
-
- /**
- * Returns whether there are options for this component available in the current setup.
- */
- public boolean isAvailable() {
- return !mOverlayPackages.isEmpty();
- }
-
- /**
- * Retrieve the available options for this component.
- * @param callback called when the themes have been retrieved (or immediately if cached)
- * @param reload whether to reload themes if they're cached.
- */
- public void fetch(OptionsFetchedListener<T> callback, boolean reload) {
- if (mOptions == null || reload) {
- mOptions = new ArrayList<>();
- loadOptions();
- }
-
- if(callback != null) {
- callback.onOptionsLoaded(mOptions);
- }
- }
-
- protected abstract void loadOptions();
-
- protected Resources getOverlayResources(String overlayPackage) throws NameNotFoundException {
- return mContext.getPackageManager().getResourcesForApplication(overlayPackage);
- }
-}
diff --git a/src/com/android/customization/model/themedicon/ThemedIconSectionController.java b/src/com/android/customization/model/themedicon/ThemedIconSectionController.java
index 5d551a6a..1cc6d0a5 100644
--- a/src/com/android/customization/model/themedicon/ThemedIconSectionController.java
+++ b/src/com/android/customization/model/themedicon/ThemedIconSectionController.java
@@ -24,10 +24,12 @@ import androidx.lifecycle.Observer;
import com.android.customization.model.themedicon.domain.interactor.ThemedIconInteractor;
import com.android.customization.model.themedicon.domain.interactor.ThemedIconSnapshotRestorer;
+import com.android.customization.module.logging.ThemesUserEventLogger;
import com.android.customization.picker.themedicon.ThemedIconSectionView;
import com.android.wallpaper.R;
import com.android.wallpaper.model.CustomizationSectionController;
+// TODO (b/311712452): Refactor CustomizationSectionController to use recommended arch UI components
/** The {@link CustomizationSectionController} for themed icon section. */
public class ThemedIconSectionController implements
CustomizationSectionController<ThemedIconSectionView> {
@@ -38,6 +40,7 @@ public class ThemedIconSectionController implements
private final ThemedIconInteractor mInteractor;
private final ThemedIconSnapshotRestorer mSnapshotRestorer;
private final Observer<Boolean> mIsActivatedChangeObserver;
+ private final ThemesUserEventLogger mThemesUserEventLogger;
private ThemedIconSectionView mThemedIconSectionView;
private boolean mSavedThemedIconEnabled = false;
@@ -46,7 +49,8 @@ public class ThemedIconSectionController implements
ThemedIconSwitchProvider themedIconOptionsProvider,
ThemedIconInteractor interactor,
@Nullable Bundle savedInstanceState,
- ThemedIconSnapshotRestorer snapshotRestorer) {
+ ThemedIconSnapshotRestorer snapshotRestorer,
+ ThemesUserEventLogger themesUserEventLogger) {
mThemedIconOptionsProvider = themedIconOptionsProvider;
mInteractor = interactor;
mSnapshotRestorer = snapshotRestorer;
@@ -55,6 +59,7 @@ public class ThemedIconSectionController implements
mThemedIconSectionView.getSwitch().setChecked(isActivated);
}
};
+ mThemesUserEventLogger = themesUserEventLogger;
if (savedInstanceState != null) {
mSavedThemedIconEnabled = savedInstanceState.getBoolean(
@@ -75,7 +80,10 @@ public class ThemedIconSectionController implements
mThemedIconSectionView.setViewListener(this::onViewActivated);
mThemedIconSectionView.getSwitch().setChecked(mSavedThemedIconEnabled);
mThemedIconOptionsProvider.fetchThemedIconEnabled(
- enabled -> mThemedIconSectionView.getSwitch().setChecked(enabled));
+ enabled -> {
+ mInteractor.setActivated(enabled);
+ mThemedIconSectionView.getSwitch().setChecked(enabled);
+ });
mInteractor.isActivatedAsLiveData().observeForever(mIsActivatedChangeObserver);
return mThemedIconSectionView;
}
@@ -91,6 +99,7 @@ public class ThemedIconSectionController implements
}
mThemedIconOptionsProvider.setThemedIconEnabled(viewActivated);
mInteractor.setActivated(viewActivated);
+ mThemesUserEventLogger.logThemedIconApplied(viewActivated);
mSnapshotRestorer.store(viewActivated);
}
diff --git a/src/com/android/customization/module/CustomizationInjector.kt b/src/com/android/customization/module/CustomizationInjector.kt
index 8fd3768d..82203d9b 100644
--- a/src/com/android/customization/module/CustomizationInjector.kt
+++ b/src/com/android/customization/module/CustomizationInjector.kt
@@ -18,33 +18,21 @@ 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
-import com.android.customization.model.theme.ThemeBundleProvider
-import com.android.customization.model.theme.ThemeManager
+import com.android.customization.module.logging.ThemesUserEventLogger
import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
import com.android.customization.picker.clock.ui.view.ClockViewFactory
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
-import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
-import com.android.customization.picker.clock.utils.ClockDescriptionUtils
import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel
import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.wallpaper.model.WallpaperColorsViewModel
import com.android.wallpaper.module.Injector
+import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository
interface CustomizationInjector : Injector {
fun getCustomizationPreferences(context: Context): CustomizationPreferences
- fun getThemeManager(
- provider: ThemeBundleProvider,
- activity: FragmentActivity,
- overlayManagerCompat: OverlayManagerCompat,
- logger: ThemesUserEventLogger,
- ): ThemeManager
-
fun getKeyguardQuickAffordancePickerInteractor(
context: Context,
): KeyguardQuickAffordancePickerInteractor
@@ -53,31 +41,28 @@ interface CustomizationInjector : Injector {
fun getClockPickerInteractor(context: Context): ClockPickerInteractor
- fun getClockSectionViewModel(
- context: Context,
- ): ClockSectionViewModel
-
fun getColorPickerInteractor(
context: Context,
- wallpaperColorsViewModel: WallpaperColorsViewModel,
+ wallpaperColorsRepository: WallpaperColorsRepository,
): ColorPickerInteractor
fun getColorPickerViewModelFactory(
context: Context,
- wallpaperColorsViewModel: WallpaperColorsViewModel,
+ wallpaperColorsRepository: WallpaperColorsRepository,
): ColorPickerViewModel.Factory
fun getClockCarouselViewModelFactory(
interactor: ClockPickerInteractor,
+ clockViewFactory: ClockViewFactory,
+ resources: Resources,
+ logger: ThemesUserEventLogger,
): ClockCarouselViewModel.Factory
fun getClockViewFactory(activity: ComponentActivity): ClockViewFactory
fun getClockSettingsViewModelFactory(
context: Context,
- wallpaperColorsViewModel: WallpaperColorsViewModel,
+ wallpaperColorsRepository: WallpaperColorsRepository,
clockViewFactory: ClockViewFactory,
): ClockSettingsViewModel.Factory
-
- fun getClockDescriptionUtils(resources: Resources): ClockDescriptionUtils
}
diff --git a/src/com/android/customization/module/CustomizationPreferences.java b/src/com/android/customization/module/CustomizationPreferences.java
deleted file mode 100644
index 0df3ff99..00000000
--- a/src/com/android/customization/module/CustomizationPreferences.java
+++ /dev/null
@@ -1,37 +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.module;
-
-import com.android.wallpaper.module.WallpaperPreferences;
-
-public interface CustomizationPreferences extends WallpaperPreferences {
-
- String KEY_CUSTOM_THEME= "themepicker_custom_theme";
- String KEY_VISITED_PREFIX = "themepicker_visited_";
- String KEY_THEMED_ICON_ENABLED = "themepicker_themed_icon_enabled";
-
- String getSerializedCustomThemes();
-
- void storeCustomThemes(String serializedCustomThemes);
-
- boolean getTabVisited(String id);
-
- void setTabVisited(String id);
-
- boolean getThemedIconEnabled();
-
- void setThemedIconEnabled(boolean enabled);
-}
diff --git a/src/com/android/customization/module/CustomizationPreferences.kt b/src/com/android/customization/module/CustomizationPreferences.kt
new file mode 100644
index 00000000..4148c3b8
--- /dev/null
+++ b/src/com/android/customization/module/CustomizationPreferences.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.module
+
+import com.android.wallpaper.module.WallpaperPreferences
+
+interface CustomizationPreferences : WallpaperPreferences {
+ fun getSerializedCustomThemes(): String?
+
+ fun storeCustomThemes(serializedCustomThemes: String)
+
+ fun getTabVisited(id: String): Boolean
+
+ fun setTabVisited(id: String)
+
+ fun getThemedIconEnabled(): Boolean
+
+ fun setThemedIconEnabled(enabled: Boolean)
+
+ companion object {
+ const val KEY_CUSTOM_THEME = "themepicker_custom_theme"
+ const val KEY_VISITED_PREFIX = "themepicker_visited_"
+ const val KEY_THEMED_ICON_ENABLED = "themepicker_themed_icon_enabled"
+ }
+}
diff --git a/src/com/android/customization/module/DefaultCustomizationPreferences.java b/src/com/android/customization/module/DefaultCustomizationPreferences.java
deleted file mode 100644
index 4af402f2..00000000
--- a/src/com/android/customization/module/DefaultCustomizationPreferences.java
+++ /dev/null
@@ -1,59 +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.module;
-
-import android.content.Context;
-
-import com.android.wallpaper.module.DefaultWallpaperPreferences;
-
-public class DefaultCustomizationPreferences extends DefaultWallpaperPreferences
- implements CustomizationPreferences {
-
- public DefaultCustomizationPreferences(Context context) {
- super(context);
- }
-
-
- @Override
- public String getSerializedCustomThemes() {
- return mSharedPrefs.getString(KEY_CUSTOM_THEME, null);
- }
-
- @Override
- public void storeCustomThemes(String serializedCustomThemes) {
- mSharedPrefs.edit().putString(KEY_CUSTOM_THEME, serializedCustomThemes).apply();
- }
-
- @Override
- public boolean getTabVisited(String id) {
- return mSharedPrefs.getBoolean(KEY_VISITED_PREFIX + id, false);
- }
-
- @Override
- public void setTabVisited(String id) {
- mSharedPrefs.edit().putBoolean(KEY_VISITED_PREFIX + id, true).apply();
- }
-
- @Override
- public boolean getThemedIconEnabled() {
- return mSharedPrefs.getBoolean(KEY_THEMED_ICON_ENABLED, false);
- }
-
- @Override
- public void setThemedIconEnabled(boolean enabled) {
- mSharedPrefs.edit().putBoolean(KEY_THEMED_ICON_ENABLED, enabled).apply();
- }
-}
diff --git a/src/com/android/customization/module/DefaultCustomizationPreferences.kt b/src/com/android/customization/module/DefaultCustomizationPreferences.kt
new file mode 100644
index 00000000..49fd1a94
--- /dev/null
+++ b/src/com/android/customization/module/DefaultCustomizationPreferences.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.module
+
+import android.content.Context
+import com.android.wallpaper.module.DefaultWallpaperPreferences
+
+open class DefaultCustomizationPreferences(context: Context) :
+ DefaultWallpaperPreferences(context), CustomizationPreferences {
+
+ override fun getSerializedCustomThemes(): String? {
+ return sharedPrefs.getString(CustomizationPreferences.KEY_CUSTOM_THEME, null)
+ }
+
+ override fun storeCustomThemes(serializedCustomThemes: String) {
+ sharedPrefs
+ .edit()
+ .putString(CustomizationPreferences.KEY_CUSTOM_THEME, serializedCustomThemes)
+ .apply()
+ }
+
+ override fun getTabVisited(id: String): Boolean {
+ return sharedPrefs.getBoolean(CustomizationPreferences.KEY_VISITED_PREFIX + id, false)
+ }
+
+ override fun setTabVisited(id: String) {
+ sharedPrefs
+ .edit()
+ .putBoolean(CustomizationPreferences.KEY_VISITED_PREFIX + id, true)
+ .apply()
+ }
+
+ override fun getThemedIconEnabled(): Boolean {
+ return sharedPrefs.getBoolean(CustomizationPreferences.KEY_THEMED_ICON_ENABLED, false)
+ }
+
+ override fun setThemedIconEnabled(enabled: Boolean) {
+ sharedPrefs
+ .edit()
+ .putBoolean(CustomizationPreferences.KEY_THEMED_ICON_ENABLED, enabled)
+ .apply()
+ }
+}
diff --git a/src/com/android/customization/module/DefaultCustomizationSections.java b/src/com/android/customization/module/DefaultCustomizationSections.java
index bbe6bef1..8347d03c 100644
--- a/src/com/android/customization/module/DefaultCustomizationSections.java
+++ b/src/com/android/customization/module/DefaultCustomizationSections.java
@@ -9,22 +9,21 @@ import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import com.android.customization.model.grid.GridOptionsManager;
-import com.android.customization.model.grid.GridSectionController;
-import com.android.customization.model.mode.DarkModeSnapshotRestorer;
import com.android.customization.model.themedicon.ThemedIconSectionController;
import com.android.customization.model.themedicon.ThemedIconSwitchProvider;
import com.android.customization.model.themedicon.domain.interactor.ThemedIconInteractor;
import com.android.customization.model.themedicon.domain.interactor.ThemedIconSnapshotRestorer;
+import com.android.customization.module.logging.ThemesUserEventLogger;
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.section.ColorSectionController;
import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel;
+import com.android.customization.picker.grid.ui.section.GridSectionController;
import com.android.customization.picker.notifications.ui.section.NotificationSectionController;
import com.android.customization.picker.notifications.ui.viewmodel.NotificationSectionViewModel;
import com.android.customization.picker.preview.ui.section.PreviewWithClockCarouselSectionController;
import com.android.customization.picker.preview.ui.section.PreviewWithThemeSectionController;
-import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor;
import com.android.customization.picker.quickaffordance.ui.section.KeyguardQuickAffordanceSectionController;
import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel;
import com.android.customization.picker.settings.ui.section.MoreSettingsSectionController;
@@ -32,10 +31,10 @@ import com.android.wallpaper.config.BaseFlags;
import com.android.wallpaper.model.CustomizationSectionController;
import com.android.wallpaper.model.CustomizationSectionController.CustomizationSectionNavigationController;
import com.android.wallpaper.model.PermissionRequester;
-import com.android.wallpaper.model.WallpaperColorsViewModel;
import com.android.wallpaper.model.WallpaperPreviewNavigator;
import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
import com.android.wallpaper.module.CustomizationSections;
+import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository;
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor;
import com.android.wallpaper.picker.customization.ui.section.ConnectedSectionController;
import com.android.wallpaper.picker.customization.ui.section.WallpaperQuickSwitchSectionController;
@@ -49,43 +48,40 @@ import java.util.List;
public final class DefaultCustomizationSections implements CustomizationSections {
private final ColorPickerViewModel.Factory mColorPickerViewModelFactory;
- private final KeyguardQuickAffordancePickerInteractor mKeyguardQuickAffordancePickerInteractor;
private final KeyguardQuickAffordancePickerViewModel.Factory
mKeyguardQuickAffordancePickerViewModelFactory;
private final NotificationSectionViewModel.Factory mNotificationSectionViewModelFactory;
private final BaseFlags mFlags;
private final ClockCarouselViewModel.Factory mClockCarouselViewModelFactory;
private final ClockViewFactory mClockViewFactory;
- private final DarkModeSnapshotRestorer mDarkModeSnapshotRestorer;
private final ThemedIconSnapshotRestorer mThemedIconSnapshotRestorer;
private final ThemedIconInteractor mThemedIconInteractor;
private final ColorPickerInteractor mColorPickerInteractor;
+ private final ThemesUserEventLogger mThemesUserEventLogger;
public DefaultCustomizationSections(
ColorPickerViewModel.Factory colorPickerViewModelFactory,
- KeyguardQuickAffordancePickerInteractor keyguardQuickAffordancePickerInteractor,
KeyguardQuickAffordancePickerViewModel.Factory
keyguardQuickAffordancePickerViewModelFactory,
NotificationSectionViewModel.Factory notificationSectionViewModelFactory,
BaseFlags flags,
ClockCarouselViewModel.Factory clockCarouselViewModelFactory,
ClockViewFactory clockViewFactory,
- DarkModeSnapshotRestorer darkModeSnapshotRestorer,
ThemedIconSnapshotRestorer themedIconSnapshotRestorer,
ThemedIconInteractor themedIconInteractor,
- ColorPickerInteractor colorPickerInteractor) {
+ ColorPickerInteractor colorPickerInteractor,
+ ThemesUserEventLogger themesUserEventLogger) {
mColorPickerViewModelFactory = colorPickerViewModelFactory;
- mKeyguardQuickAffordancePickerInteractor = keyguardQuickAffordancePickerInteractor;
mKeyguardQuickAffordancePickerViewModelFactory =
keyguardQuickAffordancePickerViewModelFactory;
mNotificationSectionViewModelFactory = notificationSectionViewModelFactory;
mFlags = flags;
mClockCarouselViewModelFactory = clockCarouselViewModelFactory;
mClockViewFactory = clockViewFactory;
- mDarkModeSnapshotRestorer = darkModeSnapshotRestorer;
mThemedIconSnapshotRestorer = themedIconSnapshotRestorer;
mThemedIconInteractor = themedIconInteractor;
mColorPickerInteractor = colorPickerInteractor;
+ mThemesUserEventLogger = themesUserEventLogger;
}
@Override
@@ -93,7 +89,7 @@ public final class DefaultCustomizationSections implements CustomizationSections
Screen screen,
FragmentActivity activity,
LifecycleOwner lifecycleOwner,
- WallpaperColorsViewModel wallpaperColorsViewModel,
+ WallpaperColorsRepository wallpaperColorsRepository,
PermissionRequester permissionRequester,
WallpaperPreviewNavigator wallpaperPreviewNavigator,
CustomizationSectionNavigationController sectionNavigationController,
@@ -114,7 +110,7 @@ public final class DefaultCustomizationSections implements CustomizationSections
lifecycleOwner,
screen,
wallpaperInfoFactory,
- wallpaperColorsViewModel,
+ wallpaperColorsRepository,
displayUtils,
mClockCarouselViewModelFactory,
mClockViewFactory,
@@ -131,7 +127,7 @@ public final class DefaultCustomizationSections implements CustomizationSections
lifecycleOwner,
screen,
wallpaperInfoFactory,
- wallpaperColorsViewModel,
+ wallpaperColorsRepository,
displayUtils,
wallpaperPreviewNavigator,
wallpaperInteractor,
@@ -144,7 +140,7 @@ public final class DefaultCustomizationSections implements CustomizationSections
sectionControllers.add(
new ConnectedSectionController(
// Theme color section.
- new ColorSectionController2(
+ new ColorSectionController(
sectionNavigationController,
new ViewModelProvider(
activity,
@@ -166,7 +162,6 @@ public final class DefaultCustomizationSections implements CustomizationSections
sectionControllers.add(
new KeyguardQuickAffordanceSectionController(
sectionNavigationController,
- mKeyguardQuickAffordancePickerInteractor,
new ViewModelProvider(
activity,
mKeyguardQuickAffordancePickerViewModelFactory)
@@ -193,7 +188,8 @@ public final class DefaultCustomizationSections implements CustomizationSections
ThemedIconSwitchProvider.getInstance(activity),
mThemedIconInteractor,
savedInstanceState,
- mThemedIconSnapshotRestorer));
+ mThemedIconSnapshotRestorer,
+ mThemesUserEventLogger));
// App grid section.
sectionControllers.add(
diff --git a/src/com/android/customization/module/StatsLogUserEventLogger.java b/src/com/android/customization/module/StatsLogUserEventLogger.java
deleted file mode 100644
index 647cdc9a..00000000
--- a/src/com/android/customization/module/StatsLogUserEventLogger.java
+++ /dev/null
@@ -1,258 +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.module;
-
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
-import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__ACTION__APP_LAUNCHED;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_CROP_AND_SET_ACTION;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_DEEP_LINK;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_LAUNCHER;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_LAUNCH_ICON;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_PREFERENCE_UNSPECIFIED;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_SETTINGS;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_SETTINGS_SEARCH;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_SUW;
-import static com.android.systemui.shared.system.SysUiStatsLog.STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_TIPS;
-import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SETTINGS_SEARCH;
-import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SOURCE_DEEP_LINK;
-import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SOURCE_LAUNCHER;
-import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SOURCE_SETTINGS;
-import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SOURCE_SUW;
-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.Intent;
-import android.stats.style.StyleEnums;
-import android.text.TextUtils;
-
-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.NoOpUserEventLogger;
-import com.android.wallpaper.module.WallpaperPreferences;
-import com.android.wallpaper.module.WallpaperStatusChecker;
-
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * StatsLog-backed implementation of {@link ThemesUserEventLogger}.
- */
-public class StatsLogUserEventLogger extends NoOpUserEventLogger implements ThemesUserEventLogger {
-
- private final WallpaperPreferences mPreferences;
- private final WallpaperStatusChecker mWallpaperStatusChecker;
-
- public StatsLogUserEventLogger(
- WallpaperPreferences preferences,
- WallpaperStatusChecker wallpaperStatusChecker) {
- mPreferences = preferences;
- mWallpaperStatusChecker = wallpaperStatusChecker;
- }
-
- @Override
- public void logAppLaunched(Intent launchSource) {
- new SysUiStatsLogger(STYLE_UICHANGED__ACTION__APP_LAUNCHED)
- .setLaunchedPreference(getAppLaunchSource(launchSource))
- .log();
- }
-
- @Override
- public void logResumed(boolean provisioned, boolean wallpaper) {
- new SysUiStatsLogger(StyleEnums.ONRESUME)
- .log();
- }
-
- @Override
- public void logStopped() {
- new SysUiStatsLogger(StyleEnums.ONSTOP)
- .log();
- }
-
- @Override
- public void logActionClicked(String collectionId, int actionLabelResId) {
- new SysUiStatsLogger(StyleEnums.WALLPAPER_EXPLORE)
- .setWallpaperCategoryHash(getIdHashCode(collectionId))
- .log();
- }
-
- @Override
- public void logIndividualWallpaperSelected(String collectionId) {
- new SysUiStatsLogger(StyleEnums.WALLPAPER_SELECT)
- .setWallpaperCategoryHash(getIdHashCode(collectionId))
- .log();
- }
-
- @Override
- public void logCategorySelected(String collectionId) {
- new SysUiStatsLogger(StyleEnums.WALLPAPER_OPEN_CATEGORY)
- .setWallpaperCategoryHash(getIdHashCode(collectionId))
- .log();
- }
-
- @Override
- public void logSnapshot() {
- final boolean isLockWallpaperSet = mWallpaperStatusChecker.isLockWallpaperSet();
- final String homeCollectionId = mPreferences.getHomeWallpaperCollectionId();
- final String homeRemoteId = mPreferences.getHomeWallpaperRemoteId();
- final String effects = mPreferences.getHomeWallpaperEffects();
- String homeWallpaperId = TextUtils.isEmpty(homeRemoteId)
- ? mPreferences.getHomeWallpaperServiceName() : homeRemoteId;
- String lockCollectionId = isLockWallpaperSet ? mPreferences.getLockWallpaperCollectionId()
- : homeCollectionId;
- String lockWallpaperId = isLockWallpaperSet ? mPreferences.getLockWallpaperRemoteId()
- : homeWallpaperId;
-
- new SysUiStatsLogger(StyleEnums.SNAPSHOT)
- .setWallpaperCategoryHash(getIdHashCode(homeCollectionId))
- .setWallpaperIdHash(getIdHashCode(homeWallpaperId))
- .setLockWallpaperCategoryHash(getIdHashCode(lockCollectionId))
- .setLockWallpaperIdHash(getIdHashCode(lockWallpaperId))
- .setFirstLaunchDateSinceSetup(mPreferences.getFirstLaunchDateSinceSetup())
- .setFirstWallpaperApplyDateSinceSetup(
- mPreferences.getFirstWallpaperApplyDateSinceSetup())
- .setAppLaunchCount(mPreferences.getAppLaunchCount())
- .setEffectIdHash(getIdHashCode(effects))
- .log();
- }
-
- @Override
- public void logWallpaperSet(String collectionId, @Nullable String wallpaperId,
- @Nullable String effects) {
- new SysUiStatsLogger(StyleEnums.WALLPAPER_APPLIED)
- .setWallpaperCategoryHash(getIdHashCode(collectionId))
- .setWallpaperIdHash(getIdHashCode(wallpaperId))
- .setEffectIdHash(getIdHashCode(effects))
- .log();
- }
-
- @Override
- public void logEffectApply(String effect, @EffectStatus int status, long timeElapsedMillis,
- int resultCode) {
- new SysUiStatsLogger(StyleEnums.WALLPAPER_EFFECT_APPLIED)
- .setEffectPreference(status)
- .setEffectIdHash(getIdHashCode(effect))
- .setTimeElapsed(timeElapsedMillis)
- .setEffectResultCode(resultCode)
- .log();
- }
-
- @Override
- public void logEffectProbe(String effect, @EffectStatus int status) {
- 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();
- }
-
- @Nullable
- private String getThemePackage(ThemeBundle theme, String category) {
- Map<String, String> packages = theme.getPackagesByCategory();
- return packages.get(category);
- }
-
- @Override
- public void logThemeSelected(ThemeBundle theme, boolean isCustomTheme) {
- new SysUiStatsLogger(StyleEnums.PICKER_SELECT)
- .setColorPackageHash(
- Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_COLOR)))
- .setFontPackageHash(Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_FONT)))
- .setShapePackageHash(
- Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_SHAPE)))
- .log();
- }
-
- @Override
- public void logThemeApplied(ThemeBundle theme, boolean isCustomTheme) {
- new SysUiStatsLogger(StyleEnums.PICKER_APPLIED)
- .setColorPackageHash(
- Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_COLOR)))
- .setFontPackageHash(Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_FONT)))
- .setShapePackageHash(
- Objects.hashCode(getThemePackage(theme, OVERLAY_CATEGORY_SHAPE)))
- .log();
- }
-
- @Override
- public void logColorApplied(int action, ColorOption colorOption) {
- new SysUiStatsLogger(action)
- .setColorPreference(colorOption.getIndex())
- .setColorVariant(colorOption.getStyle().ordinal() + 1)
- .log();
- }
-
- @Override
- public void logGridSelected(GridOption grid) {
- new SysUiStatsLogger(StyleEnums.PICKER_SELECT)
- .setLauncherGrid(grid.cols)
- .log();
- }
-
- @Override
- public void logGridApplied(GridOption grid) {
- new SysUiStatsLogger(StyleEnums.PICKER_APPLIED)
- .setLauncherGrid(grid.cols)
- .log();
- }
-
- private int getAppLaunchSource(Intent launchSource) {
- if (launchSource.hasExtra(WALLPAPER_LAUNCH_SOURCE)) {
- switch (launchSource.getStringExtra(WALLPAPER_LAUNCH_SOURCE)) {
- case LAUNCH_SOURCE_LAUNCHER:
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_LAUNCHER;
- case LAUNCH_SOURCE_SETTINGS:
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_SETTINGS;
- case LAUNCH_SOURCE_SUW:
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_SUW;
- case LAUNCH_SOURCE_TIPS:
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_TIPS;
- case LAUNCH_SOURCE_DEEP_LINK:
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_DEEP_LINK;
- default:
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_PREFERENCE_UNSPECIFIED;
- }
- } else if (launchSource.hasExtra(LAUNCH_SETTINGS_SEARCH)) {
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_SETTINGS_SEARCH;
- } else if (launchSource.getAction() != null && launchSource.getAction().equals(
- WallpaperManager.ACTION_CROP_AND_SET_WALLPAPER)) {
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_CROP_AND_SET_ACTION;
- } else if (launchSource.getCategories() != null
- && launchSource.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_LAUNCH_ICON;
- } else {
- return STYLE_UICHANGED__LAUNCHED_PREFERENCE__LAUNCHED_PREFERENCE_UNSPECIFIED;
- }
- }
-
- private int getIdHashCode(String id) {
- return id != null ? id.hashCode() : 0;
- }
-}
diff --git a/src/com/android/customization/module/ThemePickerInjector.kt b/src/com/android/customization/module/ThemePickerInjector.kt
index 497456f6..f22e562b 100644
--- a/src/com/android/customization/module/ThemePickerInjector.kt
+++ b/src/com/android/customization/module/ThemePickerInjector.kt
@@ -15,7 +15,9 @@
*/
package com.android.customization.module
+import android.app.Activity
import android.app.UiModeManager
+import android.app.WallpaperColors
import android.app.WallpaperManager
import android.content.Context
import android.content.Intent
@@ -23,40 +25,37 @@ import android.content.res.Resources
import android.net.Uri
import android.text.TextUtils
import androidx.activity.ComponentActivity
-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.color.ThemedWallpaperColorResources
+import com.android.customization.model.color.WallpaperColorResources
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
-import com.android.customization.model.grid.domain.interactor.GridSnapshotRestorer
-import com.android.customization.model.grid.ui.viewmodel.GridScreenViewModel
import com.android.customization.model.mode.DarkModeSnapshotRestorer
import com.android.customization.model.theme.OverlayManagerCompat
-import com.android.customization.model.theme.ThemeBundleProvider
-import com.android.customization.model.theme.ThemeManager
import com.android.customization.model.themedicon.ThemedIconSwitchProvider
import com.android.customization.model.themedicon.data.repository.ThemeIconRepository
import com.android.customization.model.themedicon.domain.interactor.ThemedIconInteractor
import com.android.customization.model.themedicon.domain.interactor.ThemedIconSnapshotRestorer
+import com.android.customization.module.logging.ThemesUserEventLogger
import com.android.customization.picker.clock.data.repository.ClockPickerRepositoryImpl
import com.android.customization.picker.clock.data.repository.ClockRegistryProvider
import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
import com.android.customization.picker.clock.domain.interactor.ClockPickerSnapshotRestorer
import com.android.customization.picker.clock.ui.view.ClockViewFactory
+import com.android.customization.picker.clock.ui.view.ClockViewFactoryImpl
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
-import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
-import com.android.customization.picker.clock.utils.ClockDescriptionUtils
-import com.android.customization.picker.clock.utils.ThemePickerClockDescriptionUtils
import com.android.customization.picker.color.data.repository.ColorPickerRepositoryImpl
import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
import com.android.customization.picker.color.domain.interactor.ColorPickerSnapshotRestorer
import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel
+import com.android.customization.picker.grid.data.repository.GridRepositoryImpl
+import com.android.customization.picker.grid.domain.interactor.GridInteractor
+import com.android.customization.picker.grid.domain.interactor.GridSnapshotRestorer
+import com.android.customization.picker.grid.ui.viewmodel.GridScreenViewModel
import com.android.customization.picker.notifications.data.repository.NotificationsRepository
import com.android.customization.picker.notifications.domain.interactor.NotificationsInteractor
import com.android.customization.picker.notifications.domain.interactor.NotificationsSnapshotRestorer
@@ -69,17 +68,16 @@ 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.config.BaseFlags
-import com.android.wallpaper.dispatchers.BackgroundDispatcher
-import com.android.wallpaper.dispatchers.MainDispatcher
-import com.android.wallpaper.model.WallpaperColorsViewModel
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.picker.CustomizationPickerActivity
import com.android.wallpaper.picker.customization.data.content.WallpaperClientImpl
+import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository
import com.android.wallpaper.picker.customization.data.repository.WallpaperRepository
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
+import com.android.wallpaper.picker.di.modules.BackgroundDispatcher
+import com.android.wallpaper.picker.di.modules.MainDispatcher
import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
import com.android.wallpaper.util.ScreenSizeCalculator
import javax.inject.Inject
@@ -94,9 +92,9 @@ internal constructor(
@MainDispatcher private val mainScope: CoroutineScope,
@MainDispatcher private val mainDispatcher: CoroutineDispatcher,
@BackgroundDispatcher private val bgDispatcher: CoroutineDispatcher,
-) : WallpaperPicker2Injector(mainScope, bgDispatcher), CustomizationInjector {
+ private val userEventLogger: ThemesUserEventLogger,
+) : WallpaperPicker2Injector(mainScope, bgDispatcher, userEventLogger), CustomizationInjector {
private var customizationSections: CustomizationSections? = null
- private var userEventLogger: UserEventLogger? = null
private var wallpaperInteractor: WallpaperInteractor? = null
private var keyguardQuickAffordancePickerInteractor: KeyguardQuickAffordancePickerInteractor? =
null
@@ -109,9 +107,8 @@ internal constructor(
null
private var notificationsSnapshotRestorer: NotificationsSnapshotRestorer? = null
private var clockPickerInteractor: ClockPickerInteractor? = null
- private var clockSectionViewModel: ClockSectionViewModel? = null
private var clockCarouselViewModelFactory: ClockCarouselViewModel.Factory? = null
- private var clockViewFactories: MutableMap<Int, ClockViewFactory> = HashMap()
+ private var clockViewFactory: ClockViewFactory? = null
private var clockPickerSnapshotRestorer: ClockPickerSnapshotRestorer? = null
private var notificationsInteractor: NotificationsInteractor? = null
private var notificationSectionViewModelFactory: NotificationSectionViewModel.Factory? = null
@@ -123,33 +120,35 @@ internal constructor(
private var themedIconSnapshotRestorer: ThemedIconSnapshotRestorer? = null
private var themedIconInteractor: ThemedIconInteractor? = null
private var clockSettingsViewModelFactory: ClockSettingsViewModel.Factory? = null
- private var clockDescriptionUtils: ClockDescriptionUtils? = null
private var gridInteractor: GridInteractor? = null
private var gridSnapshotRestorer: GridSnapshotRestorer? = null
private var gridScreenViewModelFactory: GridScreenViewModel.Factory? = null
private var clockRegistryProvider: ClockRegistryProvider? = null
override fun getCustomizationSections(activity: ComponentActivity): CustomizationSections {
+ val appContext = activity.applicationContext
+ val clockViewFactory = getClockViewFactory(activity)
+ val resources = activity.resources
return customizationSections
?: DefaultCustomizationSections(
getColorPickerViewModelFactory(
- context = activity,
- wallpaperColorsViewModel = getWallpaperColorsViewModel(),
- ),
- getKeyguardQuickAffordancePickerInteractor(activity),
- getKeyguardQuickAffordancePickerViewModelFactory(activity),
- NotificationSectionViewModel.Factory(
- interactor = getNotificationsInteractor(activity),
+ context = appContext,
+ wallpaperColorsRepository = getWallpaperColorsRepository(),
),
+ getKeyguardQuickAffordancePickerViewModelFactory(appContext),
+ getNotificationSectionViewModelFactory(appContext),
getFlags(),
getClockCarouselViewModelFactory(
- getClockPickerInteractor(activity.applicationContext),
+ interactor = getClockPickerInteractor(appContext),
+ clockViewFactory = clockViewFactory,
+ resources = resources,
+ logger = userEventLogger,
),
- getClockViewFactory(activity),
- getDarkModeSnapshotRestorer(activity),
- getThemedIconSnapshotRestorer(activity),
+ clockViewFactory,
+ getThemedIconSnapshotRestorer(appContext),
getThemedIconInteractor(),
- getColorPickerInteractor(activity, getWallpaperColorsViewModel()),
+ getColorPickerInteractor(appContext, getWallpaperColorsRepository()),
+ getUserEventLogger(appContext),
)
.also { customizationSections = it }
}
@@ -168,12 +167,7 @@ internal constructor(
@Synchronized
override fun getUserEventLogger(context: Context): ThemesUserEventLogger {
- return userEventLogger as? ThemesUserEventLogger
- ?: StatsLogUserEventLogger(
- getPreferences(context.applicationContext),
- getWallpaperStatusChecker(context.applicationContext),
- )
- .also { userEventLogger = it }
+ return userEventLogger
}
override fun getFragmentFactory(): FragmentFactory? {
@@ -195,7 +189,7 @@ internal constructor(
this[KEY_THEMED_ICON_SNAPSHOT_RESTORER] = getThemedIconSnapshotRestorer(context)
this[KEY_APP_GRID_SNAPSHOT_RESTORER] = getGridSnapshotRestorer(context)
this[KEY_COLOR_PICKER_SNAPSHOT_RESTORER] =
- getColorPickerSnapshotRestorer(context, getWallpaperColorsViewModel())
+ getColorPickerSnapshotRestorer(context, getWallpaperColorsRepository())
this[KEY_CLOCKS_SNAPSHOT_RESTORER] = getClockPickerSnapshotRestorer(context)
}
}
@@ -204,16 +198,11 @@ internal constructor(
return getPreferences(context) as CustomizationPreferences
}
- override fun getThemeManager(
- provider: ThemeBundleProvider,
- activity: FragmentActivity,
- overlayManagerCompat: OverlayManagerCompat,
- logger: ThemesUserEventLogger
- ): ThemeManager {
- return ThemeManager(provider, activity, overlayManagerCompat, logger)
- }
-
override fun getWallpaperInteractor(context: Context): WallpaperInteractor {
+ if (getFlags().isMultiCropEnabled() && getFlags().isMultiCropPreviewUiEnabled()) {
+ return injectedWallpaperInteractor
+ }
+
val appContext = context.applicationContext
return wallpaperInteractor
?: WallpaperInteractor(
@@ -223,8 +212,8 @@ internal constructor(
client =
WallpaperClientImpl(
context = appContext,
- infoFactory = getCurrentWallpaperInfoFactory(appContext),
- wallpaperManager = WallpaperManager.getInstance(appContext)
+ wallpaperManager = WallpaperManager.getInstance(appContext),
+ wallpaperPreferences = getPreferences(appContext)
),
wallpaperPreferences = getPreferences(context = appContext),
backgroundDispatcher = bgDispatcher,
@@ -232,7 +221,7 @@ internal constructor(
shouldHandleReload = {
TextUtils.equals(
getColorCustomizationManager(appContext).currentColorSource,
- ColorOptionsProvider.COLOR_SOURCE_PRESET
+ COLOR_SOURCE_PRESET,
)
}
)
@@ -257,6 +246,7 @@ internal constructor(
getKeyguardQuickAffordancePickerInteractor(context),
getWallpaperInteractor(context),
getCurrentWallpaperInfoFactory(context),
+ getUserEventLogger(context),
)
.also { keyguardQuickAffordancePickerViewModelFactory = it }
}
@@ -267,7 +257,7 @@ internal constructor(
val client = getKeyguardQuickAffordancePickerProviderClient(context)
val appContext = context.applicationContext
return KeyguardQuickAffordancePickerInteractor(
- KeyguardQuickAffordancePickerRepository(client, bgDispatcher),
+ KeyguardQuickAffordancePickerRepository(client, getApplicationCoroutineScope()),
client
) {
getKeyguardQuickAffordanceSnapshotRestorer(appContext)
@@ -300,6 +290,7 @@ internal constructor(
return notificationSectionViewModelFactory
?: NotificationSectionViewModel.Factory(
interactor = getNotificationsInteractor(context),
+ logger = getUserEventLogger(context),
)
.also { notificationSectionViewModelFactory = it }
}
@@ -362,30 +353,26 @@ internal constructor(
.also { clockPickerInteractor = it }
}
- override fun getClockSectionViewModel(
- context: Context,
- ): ClockSectionViewModel {
- return clockSectionViewModel
- ?: ClockSectionViewModel(
- context.applicationContext,
- getClockPickerInteractor(context.applicationContext)
- )
- .also { clockSectionViewModel = it }
- }
-
override fun getClockCarouselViewModelFactory(
interactor: ClockPickerInteractor,
+ clockViewFactory: ClockViewFactory,
+ resources: Resources,
+ logger: ThemesUserEventLogger,
): ClockCarouselViewModel.Factory {
return clockCarouselViewModelFactory
- ?: ClockCarouselViewModel.Factory(interactor, bgDispatcher).also {
- clockCarouselViewModelFactory = it
- }
+ ?: ClockCarouselViewModel.Factory(
+ interactor,
+ bgDispatcher,
+ clockViewFactory,
+ resources,
+ logger,
+ )
+ .also { clockCarouselViewModelFactory = it }
}
override fun getClockViewFactory(activity: ComponentActivity): ClockViewFactory {
- val activityHashCode = activity.hashCode()
- return clockViewFactories[activityHashCode]
- ?: ClockViewFactory(
+ return clockViewFactory
+ ?: ClockViewFactoryImpl(
activity.applicationContext,
ScreenSizeCalculator.getInstance()
.getScreenSize(activity.windowManager.defaultDisplay),
@@ -393,13 +380,13 @@ internal constructor(
getClockRegistry(activity.applicationContext),
)
.also {
- clockViewFactories[activityHashCode] = it
+ clockViewFactory = it
activity.lifecycle.addObserver(
object : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
- clockViewFactories[activityHashCode]?.onDestroy()
- clockViewFactories.remove(activityHashCode)
+ if ((owner as Activity).isChangingConfigurations()) return
+ clockViewFactory?.onDestroy()
}
}
)
@@ -415,20 +402,27 @@ internal constructor(
}
}
+ override fun getWallpaperColorResources(
+ wallpaperColors: WallpaperColors,
+ context: Context
+ ): WallpaperColorResources {
+ return ThemedWallpaperColorResources(wallpaperColors, context)
+ }
+
override fun getColorPickerInteractor(
context: Context,
- wallpaperColorsViewModel: WallpaperColorsViewModel,
+ wallpaperColorsRepository: WallpaperColorsRepository,
): ColorPickerInteractor {
val appContext = context.applicationContext
return colorPickerInteractor
?: ColorPickerInteractor(
repository =
ColorPickerRepositoryImpl(
- wallpaperColorsViewModel,
+ wallpaperColorsRepository,
getColorCustomizationManager(appContext)
),
snapshotRestorer = {
- getColorPickerSnapshotRestorer(appContext, wallpaperColorsViewModel)
+ getColorPickerSnapshotRestorer(appContext, wallpaperColorsRepository)
}
)
.also { colorPickerInteractor = it }
@@ -436,23 +430,24 @@ internal constructor(
override fun getColorPickerViewModelFactory(
context: Context,
- wallpaperColorsViewModel: WallpaperColorsViewModel,
+ wallpaperColorsRepository: WallpaperColorsRepository,
): ColorPickerViewModel.Factory {
return colorPickerViewModelFactory
?: ColorPickerViewModel.Factory(
context.applicationContext,
- getColorPickerInteractor(context, wallpaperColorsViewModel),
+ getColorPickerInteractor(context, wallpaperColorsRepository),
+ userEventLogger,
)
.also { colorPickerViewModelFactory = it }
}
private fun getColorPickerSnapshotRestorer(
context: Context,
- wallpaperColorsViewModel: WallpaperColorsViewModel,
+ wallpaperColorsRepository: WallpaperColorsRepository,
): ColorPickerSnapshotRestorer {
return colorPickerSnapshotRestorer
?: ColorPickerSnapshotRestorer(
- getColorPickerInteractor(context, wallpaperColorsViewModel)
+ getColorPickerInteractor(context, wallpaperColorsRepository)
)
.also { colorPickerSnapshotRestorer = it }
}
@@ -502,7 +497,7 @@ internal constructor(
override fun getClockSettingsViewModelFactory(
context: Context,
- wallpaperColorsViewModel: WallpaperColorsViewModel,
+ wallpaperColorsRepository: WallpaperColorsRepository,
clockViewFactory: ClockViewFactory,
): ClockSettingsViewModel.Factory {
return clockSettingsViewModelFactory
@@ -511,8 +506,9 @@ internal constructor(
getClockPickerInteractor(context),
getColorPickerInteractor(
context,
- wallpaperColorsViewModel,
+ wallpaperColorsRepository,
),
+ userEventLogger,
) { clockId ->
clockId?.let { clockViewFactory.getController(clockId).config.isReactiveToTone }
?: false
@@ -520,11 +516,6 @@ internal constructor(
.also { clockSettingsViewModelFactory = it }
}
- override fun getClockDescriptionUtils(resources: Resources): ClockDescriptionUtils {
- return clockDescriptionUtils
- ?: ThemePickerClockDescriptionUtils().also { clockDescriptionUtils = it }
- }
-
fun getGridScreenViewModelFactory(
context: Context,
): ViewModelProvider.Factory {
diff --git a/src/com/android/customization/module/ThemesUserEventLogger.java b/src/com/android/customization/module/ThemesUserEventLogger.java
deleted file mode 100644
index b1a87b9c..00000000
--- a/src/com/android/customization/module/ThemesUserEventLogger.java
+++ /dev/null
@@ -1,44 +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.module;
-
-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.UserEventLogger;
-
-/**
- * Extension of {@link UserEventLogger} that adds ThemePicker specific events.
- */
-public interface ThemesUserEventLogger extends UserEventLogger {
-
- void logThemeSelected(ThemeBundle theme, boolean isCustomTheme);
-
- void logThemeApplied(ThemeBundle theme, boolean isCustomTheme);
-
- /**
- * Logs the color usage while color is applied.
- *
- * @param action color applied action.
- * @param colorOption applied color option.
- */
- void logColorApplied(int action, ColorOption colorOption);
-
- void logGridSelected(GridOption grid);
-
- void logGridApplied(GridOption grid);
-
-}
diff --git a/src/com/android/customization/module/logging/AppSessionId.kt b/src/com/android/customization/module/logging/AppSessionId.kt
new file mode 100644
index 00000000..c831f225
--- /dev/null
+++ b/src/com/android/customization/module/logging/AppSessionId.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.module.logging
+
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class AppSessionId @Inject constructor() {
+
+ private var sessionId: InstanceId = newInstanceId()
+
+ fun createNewId(): AppSessionId {
+ sessionId = newInstanceId()
+ return this
+ }
+
+ fun getId(): Int {
+ return sessionId.hashCode()
+ }
+
+ private fun newInstanceId(): InstanceId = InstanceIdSequence(INSTANCE_ID_MAX).newInstanceId()
+
+ companion object {
+ // At most 20 bits: ~1m possibilities, ~0.5% probability of collision in 100 values
+ private const val INSTANCE_ID_MAX = 1 shl 20
+ }
+}
diff --git a/src/com/android/customization/module/SysUiStatsLogger.kt b/src/com/android/customization/module/logging/SysUiStatsLogger.kt
index 8e97b0b6..111c2c23 100644
--- a/src/com/android/customization/module/SysUiStatsLogger.kt
+++ b/src/com/android/customization/module/logging/SysUiStatsLogger.kt
@@ -13,11 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.customization.module
-
-import android.stats.style.StyleEnums
+package com.android.customization.module.logging
+
+import android.stats.style.StyleEnums.CLOCK_SIZE_UNSPECIFIED
+import android.stats.style.StyleEnums.COLOR_SOURCE_UNSPECIFIED
+import android.stats.style.StyleEnums.DATE_PREFERENCE_UNSPECIFIED
+import android.stats.style.StyleEnums.EFFECT_PREFERENCE_UNSPECIFIED
+import android.stats.style.StyleEnums.LAUNCHED_PREFERENCE_UNSPECIFIED
+import android.stats.style.StyleEnums.LOCATION_PREFERENCE_UNSPECIFIED
+import android.stats.style.StyleEnums.SET_WALLPAPER_ENTRY_POINT_UNSPECIFIED
+import android.stats.style.StyleEnums.WALLPAPER_DESTINATION_UNSPECIFIED
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.shared.system.SysUiStatsLog.STYLE_UI_CHANGED
+import com.android.wallpaper.module.logging.UserEventLogger.SetWallpaperEntryPoint
/** The builder for [SysUiStatsLog]. */
class SysUiStatsLogger(val action: Int) {
@@ -30,10 +38,10 @@ class SysUiStatsLogger(val action: Int) {
private var wallpaperCategoryHash = 0
private var wallpaperIdHash = 0
private var colorPreference = 0
- private var locationPreference = StyleEnums.EFFECT_PREFERENCE_UNSPECIFIED
- private var datePreference = StyleEnums.DATE_PREFERENCE_UNSPECIFIED
- private var launchedPreference = StyleEnums.LAUNCHED_PREFERENCE_UNSPECIFIED
- private var effectPreference = StyleEnums.EFFECT_PREFERENCE_UNSPECIFIED
+ private var locationPreference = LOCATION_PREFERENCE_UNSPECIFIED
+ private var datePreference = DATE_PREFERENCE_UNSPECIFIED
+ private var launchedPreference = LAUNCHED_PREFERENCE_UNSPECIFIED
+ private var effectPreference = EFFECT_PREFERENCE_UNSPECIFIED
private var effectIdHash = 0
private var lockWallpaperCategoryHash = 0
private var lockWallpaperIdHash = 0
@@ -43,14 +51,21 @@ class SysUiStatsLogger(val action: Int) {
private var colorVariant = 0
private var timeElapsedMillis = 0L
private var effectResultCode = -1
+ private var appSessionId = 0
+ private var setWallpaperEntryPoint = SET_WALLPAPER_ENTRY_POINT_UNSPECIFIED
+ private var wallpaperDestination = WALLPAPER_DESTINATION_UNSPECIFIED
+ private var colorSource = COLOR_SOURCE_UNSPECIFIED
+ private var seedColor = 0
+ private var clockSize = CLOCK_SIZE_UNSPECIFIED
+ private var toggleOn = false
+ private var shortcut = ""
+ private var shortcutSlotId = ""
fun setColorPackageHash(colorPackageHash: Int) = apply {
this.colorPackageHash = colorPackageHash
}
- fun setFontPackageHash(fontPackageHash: Int) = apply {
- this.fontPackageHash = fontPackageHash
- }
+ fun setFontPackageHash(fontPackageHash: Int) = apply { this.fontPackageHash = fontPackageHash }
fun setShapePackageHash(shapePackageHash: Int) = apply {
this.shapePackageHash = shapePackageHash
@@ -66,13 +81,9 @@ class SysUiStatsLogger(val action: Int) {
this.wallpaperCategoryHash = wallpaperCategoryHash
}
- fun setWallpaperIdHash(wallpaperIdHash: Int) = apply {
- this.wallpaperIdHash = wallpaperIdHash
- }
+ fun setWallpaperIdHash(wallpaperIdHash: Int) = apply { this.wallpaperIdHash = wallpaperIdHash }
- fun setColorPreference(colorPreference: Int) = apply {
- this.colorPreference = colorPreference
- }
+ fun setColorPreference(colorPreference: Int) = apply { this.colorPreference = colorPreference }
fun setLocationPreference(locationPreference: Int) = apply {
this.locationPreference = locationPreference
@@ -111,13 +122,35 @@ class SysUiStatsLogger(val action: Int) {
fun setColorVariant(colorVariant: Int) = apply { this.colorVariant = colorVariant }
fun setTimeElapsed(timeElapsedMillis: Long) = apply {
- this.timeElapsedMillis = timeElapsedMillis
+ this.timeElapsedMillis = timeElapsedMillis
}
fun setEffectResultCode(effectResultCode: Int) = apply {
this.effectResultCode = effectResultCode
}
+ fun setAppSessionId(sessionId: Int) = apply { this.appSessionId = sessionId }
+
+ fun setSetWallpaperEntryPoint(@SetWallpaperEntryPoint setWallpaperEntryPoint: Int) = apply {
+ this.setWallpaperEntryPoint = setWallpaperEntryPoint
+ }
+
+ fun setWallpaperDestination(wallpaperDestination: Int) = apply {
+ this.wallpaperDestination = wallpaperDestination
+ }
+
+ fun setColorSource(colorSource: Int) = apply { this.colorSource = colorSource }
+
+ fun setSeedColor(seedColor: Int) = apply { this.seedColor = seedColor }
+
+ fun setClockSize(clockSize: Int) = apply { this.clockSize = clockSize }
+
+ fun setToggleOn(toggleOn: Boolean) = apply { this.toggleOn = toggleOn }
+
+ fun setShortcut(shortcut: String) = apply { this.shortcut = shortcut }
+
+ fun setShortcutSlotId(shortcutSlotId: String) = apply { this.shortcutSlotId = shortcutSlotId }
+
fun log() {
SysUiStatsLog.write(
STYLE_UI_CHANGED,
@@ -143,6 +176,15 @@ class SysUiStatsLogger(val action: Int) {
colorVariant,
timeElapsedMillis,
effectResultCode,
+ appSessionId,
+ setWallpaperEntryPoint,
+ wallpaperDestination,
+ colorSource,
+ seedColor,
+ clockSize,
+ toggleOn,
+ shortcut,
+ shortcutSlotId,
)
}
}
diff --git a/src/com/android/customization/module/logging/ThemesUserEventLogger.kt b/src/com/android/customization/module/logging/ThemesUserEventLogger.kt
new file mode 100644
index 00000000..60fd062a
--- /dev/null
+++ b/src/com/android/customization/module/logging/ThemesUserEventLogger.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.module.logging
+
+import android.stats.style.StyleEnums
+import androidx.annotation.IntDef
+import com.android.customization.model.grid.GridOption
+import com.android.wallpaper.module.logging.UserEventLogger
+
+/** Extension of [UserEventLogger] that adds ThemePicker specific events. */
+interface ThemesUserEventLogger : UserEventLogger {
+
+ fun logThemeColorApplied(@ColorSource source: Int, style: Int, seedColor: Int)
+
+ fun logGridApplied(grid: GridOption)
+
+ fun logClockApplied(clockId: String)
+
+ fun logClockColorApplied(seedColor: Int)
+
+ fun logClockSizeApplied(@ClockSize clockSize: Int)
+
+ fun logThemedIconApplied(useThemeIcon: Boolean)
+
+ fun logLockScreenNotificationApplied(showLockScreenNotifications: Boolean)
+
+ fun logShortcutApplied(shortcut: String, shortcutSlotId: String)
+
+ fun logDarkThemeApplied(useDarkTheme: Boolean)
+
+ @IntDef(
+ StyleEnums.COLOR_SOURCE_UNSPECIFIED,
+ StyleEnums.COLOR_SOURCE_HOME_SCREEN_WALLPAPER,
+ StyleEnums.COLOR_SOURCE_LOCK_SCREEN_WALLPAPER,
+ StyleEnums.COLOR_SOURCE_PRESET_COLOR,
+ )
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class ColorSource
+
+ @IntDef(
+ StyleEnums.CLOCK_SIZE_UNSPECIFIED,
+ StyleEnums.CLOCK_SIZE_DYNAMIC,
+ StyleEnums.CLOCK_SIZE_SMALL,
+ )
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class ClockSize
+
+ companion object {
+ const val NULL_SEED_COLOR = 0
+ }
+}
diff --git a/src/com/android/customization/module/logging/ThemesUserEventLoggerImpl.kt b/src/com/android/customization/module/logging/ThemesUserEventLoggerImpl.kt
new file mode 100644
index 00000000..3f4a6dc4
--- /dev/null
+++ b/src/com/android/customization/module/logging/ThemesUserEventLoggerImpl.kt
@@ -0,0 +1,299 @@
+/*
+ * 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.module.logging
+
+import android.app.WallpaperManager
+import android.content.Intent
+import android.stats.style.StyleEnums.APP_LAUNCHED
+import android.stats.style.StyleEnums.CLOCK_APPLIED
+import android.stats.style.StyleEnums.CLOCK_COLOR_APPLIED
+import android.stats.style.StyleEnums.CLOCK_SIZE_APPLIED
+import android.stats.style.StyleEnums.DARK_THEME_APPLIED
+import android.stats.style.StyleEnums.GRID_APPLIED
+import android.stats.style.StyleEnums.LAUNCHED_CROP_AND_SET_ACTION
+import android.stats.style.StyleEnums.LAUNCHED_DEEP_LINK
+import android.stats.style.StyleEnums.LAUNCHED_KEYGUARD
+import android.stats.style.StyleEnums.LAUNCHED_LAUNCHER
+import android.stats.style.StyleEnums.LAUNCHED_LAUNCH_ICON
+import android.stats.style.StyleEnums.LAUNCHED_PREFERENCE_UNSPECIFIED
+import android.stats.style.StyleEnums.LAUNCHED_SETTINGS
+import android.stats.style.StyleEnums.LAUNCHED_SETTINGS_SEARCH
+import android.stats.style.StyleEnums.LAUNCHED_SUW
+import android.stats.style.StyleEnums.LAUNCHED_TIPS
+import android.stats.style.StyleEnums.LOCK_SCREEN_NOTIFICATION_APPLIED
+import android.stats.style.StyleEnums.RESET_APPLIED
+import android.stats.style.StyleEnums.SHORTCUT_APPLIED
+import android.stats.style.StyleEnums.SNAPSHOT
+import android.stats.style.StyleEnums.THEMED_ICON_APPLIED
+import android.stats.style.StyleEnums.THEME_COLOR_APPLIED
+import android.stats.style.StyleEnums.WALLPAPER_APPLIED
+import android.stats.style.StyleEnums.WALLPAPER_DESTINATION_HOME_AND_LOCK_SCREEN
+import android.stats.style.StyleEnums.WALLPAPER_DESTINATION_HOME_SCREEN
+import android.stats.style.StyleEnums.WALLPAPER_DESTINATION_LOCK_SCREEN
+import android.stats.style.StyleEnums.WALLPAPER_EFFECT_APPLIED
+import android.stats.style.StyleEnums.WALLPAPER_EFFECT_FG_DOWNLOAD
+import android.stats.style.StyleEnums.WALLPAPER_EFFECT_PROBE
+import android.stats.style.StyleEnums.WALLPAPER_EXPLORE
+import android.text.TextUtils
+import com.android.customization.model.color.ColorCustomizationManager
+import com.android.customization.model.grid.GridOption
+import com.android.customization.module.logging.ThemesUserEventLogger.ClockSize
+import com.android.customization.module.logging.ThemesUserEventLogger.ColorSource
+import com.android.wallpaper.module.WallpaperPreferences
+import com.android.wallpaper.module.logging.UserEventLogger.EffectStatus
+import com.android.wallpaper.module.logging.UserEventLogger.SetWallpaperEntryPoint
+import com.android.wallpaper.module.logging.UserEventLogger.WallpaperDestination
+import com.android.wallpaper.util.ActivityUtils
+import com.android.wallpaper.util.LaunchSourceUtils
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** StatsLog-backed implementation of [ThemesUserEventLogger]. */
+@Singleton
+class ThemesUserEventLoggerImpl
+@Inject
+constructor(
+ private val preferences: WallpaperPreferences,
+ private val colorManager: ColorCustomizationManager,
+ private val appSessionId: AppSessionId,
+) : ThemesUserEventLogger {
+
+ override fun logSnapshot() {
+ SysUiStatsLogger(SNAPSHOT)
+ .setWallpaperCategoryHash(preferences.getHomeCategoryHash())
+ .setWallpaperIdHash(preferences.getHomeWallpaperIdHash())
+ .setLockWallpaperCategoryHash(preferences.getLockCategoryHash())
+ .setLockWallpaperIdHash(preferences.getLockWallpaperIdHash())
+ .setEffectIdHash(preferences.getHomeWallpaperEffectsIdHash())
+ .setColorSource(colorManager.currentColorSourceForLogging)
+ .setColorVariant(colorManager.currentStyleForLogging)
+ .setSeedColor(colorManager.currentSeedColorForLogging)
+ .log()
+ }
+
+ override fun logAppLaunched(launchSource: Intent) {
+ SysUiStatsLogger(APP_LAUNCHED)
+ .setAppSessionId(appSessionId.createNewId().getId())
+ .setLaunchedPreference(launchSource.getAppLaunchSource())
+ .log()
+ }
+
+ override fun logWallpaperApplied(
+ collectionId: String?,
+ wallpaperId: String?,
+ effects: String?,
+ @SetWallpaperEntryPoint setWallpaperEntryPoint: Int,
+ @WallpaperDestination destination: Int,
+ ) {
+ val categoryHash = getIdHashCode(collectionId)
+ val wallpaperIdHash = getIdHashCode(wallpaperId)
+ val isHomeWallpaperSet =
+ destination == WALLPAPER_DESTINATION_HOME_SCREEN ||
+ destination == WALLPAPER_DESTINATION_HOME_AND_LOCK_SCREEN
+ val isLockWallpaperSet =
+ destination == WALLPAPER_DESTINATION_LOCK_SCREEN ||
+ destination == WALLPAPER_DESTINATION_HOME_AND_LOCK_SCREEN
+ SysUiStatsLogger(WALLPAPER_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setWallpaperCategoryHash(if (isHomeWallpaperSet) categoryHash else 0)
+ .setWallpaperIdHash(if (isHomeWallpaperSet) wallpaperIdHash else 0)
+ .setLockWallpaperCategoryHash(if (isLockWallpaperSet) categoryHash else 0)
+ .setLockWallpaperIdHash(if (isLockWallpaperSet) wallpaperIdHash else 0)
+ .setEffectIdHash(getIdHashCode(effects))
+ .setSetWallpaperEntryPoint(setWallpaperEntryPoint)
+ .setWallpaperDestination(destination)
+ .log()
+ }
+
+ override fun logEffectApply(
+ effect: String,
+ @EffectStatus status: Int,
+ timeElapsedMillis: Long,
+ resultCode: Int
+ ) {
+ SysUiStatsLogger(WALLPAPER_EFFECT_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setEffectPreference(status)
+ .setEffectIdHash(getIdHashCode(effect))
+ .setTimeElapsed(timeElapsedMillis)
+ .setEffectResultCode(resultCode)
+ .log()
+ }
+
+ override fun logEffectProbe(effect: String, @EffectStatus status: Int) {
+ SysUiStatsLogger(WALLPAPER_EFFECT_PROBE)
+ .setAppSessionId(appSessionId.getId())
+ .setEffectPreference(status)
+ .setEffectIdHash(getIdHashCode(effect))
+ .log()
+ }
+
+ override fun logEffectForegroundDownload(
+ effect: String,
+ @EffectStatus status: Int,
+ timeElapsedMillis: Long
+ ) {
+ SysUiStatsLogger(WALLPAPER_EFFECT_FG_DOWNLOAD)
+ .setAppSessionId(appSessionId.getId())
+ .setEffectPreference(status)
+ .setEffectIdHash(getIdHashCode(effect))
+ .setTimeElapsed(timeElapsedMillis)
+ .log()
+ }
+
+ override fun logResetApplied() {
+ SysUiStatsLogger(RESET_APPLIED).setAppSessionId(appSessionId.getId()).log()
+ }
+
+ override fun logWallpaperExploreButtonClicked() {
+ SysUiStatsLogger(WALLPAPER_EXPLORE).setAppSessionId(appSessionId.getId()).log()
+ }
+
+ override fun logThemeColorApplied(
+ @ColorSource source: Int,
+ style: Int,
+ seedColor: Int,
+ ) {
+ SysUiStatsLogger(THEME_COLOR_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setColorSource(source)
+ .setColorVariant(style)
+ .setSeedColor(seedColor)
+ .log()
+ }
+
+ override fun logGridApplied(grid: GridOption) {
+ SysUiStatsLogger(GRID_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setLauncherGrid(grid.getLauncherGridInt())
+ .log()
+ }
+
+ override fun logClockApplied(clockId: String) {
+ SysUiStatsLogger(CLOCK_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setClockPackageHash(getIdHashCode(clockId))
+ .log()
+ }
+
+ override fun logClockColorApplied(seedColor: Int) {
+ SysUiStatsLogger(CLOCK_COLOR_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setSeedColor(seedColor)
+ .log()
+ }
+
+ override fun logClockSizeApplied(@ClockSize clockSize: Int) {
+ SysUiStatsLogger(CLOCK_SIZE_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setClockSize(clockSize)
+ .log()
+ }
+
+ override fun logThemedIconApplied(useThemeIcon: Boolean) {
+ SysUiStatsLogger(THEMED_ICON_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setToggleOn(useThemeIcon)
+ .log()
+ }
+
+ override fun logLockScreenNotificationApplied(showLockScreenNotifications: Boolean) {
+ SysUiStatsLogger(LOCK_SCREEN_NOTIFICATION_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setToggleOn(showLockScreenNotifications)
+ .log()
+ }
+
+ override fun logShortcutApplied(shortcut: String, shortcutSlotId: String) {
+ SysUiStatsLogger(SHORTCUT_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setShortcut(shortcut)
+ .setShortcutSlotId(shortcutSlotId)
+ .log()
+ }
+
+ override fun logDarkThemeApplied(useDarkTheme: Boolean) {
+ SysUiStatsLogger(DARK_THEME_APPLIED)
+ .setAppSessionId(appSessionId.getId())
+ .setToggleOn(useDarkTheme)
+ .log()
+ }
+
+ /**
+ * The grid integer depends on the column and row numbers. For example: 4x5 is 405 13x37 is 1337
+ * The upper limit for the column / row count is 99.
+ */
+ private fun GridOption.getLauncherGridInt(): Int {
+ return cols * 100 + rows
+ }
+
+ private fun Intent.getAppLaunchSource(): Int {
+ return if (hasExtra(LaunchSourceUtils.WALLPAPER_LAUNCH_SOURCE)) {
+ when (getStringExtra(LaunchSourceUtils.WALLPAPER_LAUNCH_SOURCE)) {
+ LaunchSourceUtils.LAUNCH_SOURCE_LAUNCHER -> LAUNCHED_LAUNCHER
+ LaunchSourceUtils.LAUNCH_SOURCE_SETTINGS -> LAUNCHED_SETTINGS
+ LaunchSourceUtils.LAUNCH_SOURCE_SUW -> LAUNCHED_SUW
+ LaunchSourceUtils.LAUNCH_SOURCE_TIPS -> LAUNCHED_TIPS
+ LaunchSourceUtils.LAUNCH_SOURCE_DEEP_LINK -> LAUNCHED_DEEP_LINK
+ LaunchSourceUtils.LAUNCH_SOURCE_KEYGUARD -> LAUNCHED_KEYGUARD
+ else -> LAUNCHED_PREFERENCE_UNSPECIFIED
+ }
+ } else if (ActivityUtils.isLaunchedFromSettingsSearch(this)) {
+ LAUNCHED_SETTINGS_SEARCH
+ } else if (action != null && action == WallpaperManager.ACTION_CROP_AND_SET_WALLPAPER) {
+ LAUNCHED_CROP_AND_SET_ACTION
+ } else if (categories != null && categories.contains(Intent.CATEGORY_LAUNCHER)) {
+ LAUNCHED_LAUNCH_ICON
+ } else {
+ LAUNCHED_PREFERENCE_UNSPECIFIED
+ }
+ }
+
+ /** If not set, the output hash is 0. */
+ private fun WallpaperPreferences.getHomeCategoryHash(): Int {
+ return getIdHashCode(getHomeWallpaperCollectionId())
+ }
+
+ /** If not set, the output hash is 0. */
+ private fun WallpaperPreferences.getHomeWallpaperIdHash(): Int {
+ val remoteId = getHomeWallpaperRemoteId()
+ val wallpaperId =
+ if (!TextUtils.isEmpty(remoteId)) remoteId else getHomeWallpaperServiceName()
+ return getIdHashCode(wallpaperId)
+ }
+
+ /** If not set, the output hash is 0. */
+ private fun WallpaperPreferences.getLockCategoryHash(): Int {
+ return getIdHashCode(getLockWallpaperCollectionId())
+ }
+
+ /** If not set, the output hash is 0. */
+ private fun WallpaperPreferences.getLockWallpaperIdHash(): Int {
+ val remoteId = getLockWallpaperRemoteId()
+ val wallpaperId =
+ if (!TextUtils.isEmpty(remoteId)) remoteId else getLockWallpaperServiceName()
+ return getIdHashCode(wallpaperId)
+ }
+
+ /** If not set, the output hash is 0. */
+ private fun WallpaperPreferences.getHomeWallpaperEffectsIdHash(): Int {
+ return getIdHashCode(getHomeWallpaperEffects())
+ }
+
+ private fun getIdHashCode(id: String?): Int {
+ return id?.hashCode() ?: 0
+ }
+}
diff --git a/src/com/android/customization/picker/WallpaperPreviewer.java b/src/com/android/customization/picker/WallpaperPreviewer.java
index d74bfaea..18bc89c7 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, WallpaperConnection.WHICH_PREVIEW.PREVIEW_CURRENT);
+ }, mWallpaperSurface, WallpaperConnection.WhichPreview.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 004103f3..cc4079a1 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
@@ -21,7 +21,7 @@ import androidx.annotation.ColorInt
import androidx.annotation.IntRange
import com.android.customization.picker.clock.shared.ClockSize
import com.android.customization.picker.clock.shared.model.ClockMetadataModel
-import com.android.systemui.plugins.ClockMetadata
+import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.wallpaper.settings.data.repository.SecureSettingsRepository
import kotlinx.coroutines.CoroutineDispatcher
@@ -187,7 +187,6 @@ class ClockPickerRepositoryImpl(
): ClockMetadataModel {
return ClockMetadataModel(
clockId = clockId,
- name = name,
isSelected = isSelected,
selectedColorId = selectedColorId,
colorToneProgress = colorTone,
diff --git a/src/com/android/customization/picker/clock/shared/ClockSize.kt b/src/com/android/customization/picker/clock/shared/ClockSize.kt
index 279ee54b..9b35f73f 100644
--- a/src/com/android/customization/picker/clock/shared/ClockSize.kt
+++ b/src/com/android/customization/picker/clock/shared/ClockSize.kt
@@ -16,7 +16,18 @@
*/
package com.android.customization.picker.clock.shared
+import android.stats.style.StyleEnums
+import com.android.customization.module.logging.ThemesUserEventLogger
+
enum class ClockSize {
DYNAMIC,
SMALL,
}
+
+@ThemesUserEventLogger.ClockSize
+fun ClockSize.toClockSizeForLogging(): Int {
+ return when (this) {
+ ClockSize.DYNAMIC -> StyleEnums.CLOCK_SIZE_DYNAMIC
+ ClockSize.SMALL -> StyleEnums.CLOCK_SIZE_SMALL
+ }
+}
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 25225075..6e2bfb38 100644
--- a/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
+++ b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
@@ -23,7 +23,6 @@ import androidx.annotation.IntRange
/** Model for clock metadata. */
data class ClockMetadataModel(
val clockId: String,
- val name: String,
val isSelected: Boolean,
val selectedColorId: String?,
@IntRange(from = 0, to = 100) val colorToneProgress: 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 6bd717b7..e2616c76 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -58,11 +58,6 @@ object ClockCarouselViewBinder {
carouselView.transitionToPrevious()
}
)
- screenPreviewClickView.accessibilityDelegate = carouselAccessibilityDelegate
- screenPreviewClickView.setOnSideClickedListener { isStart ->
- if (isStart) carouselView.scrollToPrevious() else carouselView.scrollToNext()
- }
-
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
@@ -76,6 +71,11 @@ object ClockCarouselViewBinder {
},
isTwoPaneAndSmallWidth = isTwoPaneAndSmallWidth,
)
+ screenPreviewClickView.accessibilityDelegate = carouselAccessibilityDelegate
+ screenPreviewClickView.setOnSideClickedListener { isStart ->
+ if (isStart) carouselView.scrollToPrevious()
+ else carouselView.scrollToNext()
+ }
}
}
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockSectionViewBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockSectionViewBinder.kt
deleted file mode 100644
index 7dc0d0c4..00000000
--- a/src/com/android/customization/picker/clock/ui/binder/ClockSectionViewBinder.kt
+++ /dev/null
@@ -1,53 +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.clock.ui.binder
-
-import android.view.View
-import android.widget.TextView
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
-import com.android.wallpaper.R
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
-
-object ClockSectionViewBinder {
- fun bind(
- view: View,
- viewModel: ClockSectionViewModel,
- lifecycleOwner: LifecycleOwner,
- onClicked: () -> Unit,
- ) {
- view.setOnClickListener { onClicked() }
-
- val selectedClockColorAndSize: TextView =
- view.requireViewById(R.id.selected_clock_color_and_size)
-
- lifecycleOwner.lifecycleScope.launch {
- lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- viewModel.selectedClockColorAndSizeText.collectLatest {
- selectedClockColorAndSize.text = 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 6e745d54..d17cdf8a 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
@@ -15,11 +15,18 @@
*/
package com.android.customization.picker.clock.ui.binder
+import android.content.Context
import android.content.res.Configuration
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.style.TextAppearanceSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
+import android.widget.RadioButton
+import android.widget.RadioGroup
+import android.widget.RadioGroup.OnCheckedChangeListener
import android.widget.SeekBar
import androidx.core.view.doOnPreDraw
import androidx.core.view.isInvisible
@@ -35,7 +42,6 @@ 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
import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
import com.android.customization.picker.color.ui.binder.ColorOptionIconBinder
@@ -83,14 +89,27 @@ object ClockSettingsBinder {
}
)
- val sizeOptions =
- view.requireViewById<ClockSizeRadioButtonGroup>(R.id.clock_size_radio_button_group)
- sizeOptions.onRadioButtonClickListener =
- object : ClockSizeRadioButtonGroup.OnRadioButtonClickListener {
- override fun onClick(size: ClockSize) {
- viewModel.setClockSize(size)
- }
+ val onCheckedChangeListener = OnCheckedChangeListener { _, id ->
+ when (id) {
+ R.id.radio_dynamic -> viewModel.setClockSize(ClockSize.DYNAMIC)
+ R.id.radio_small -> viewModel.setClockSize(ClockSize.SMALL)
}
+ }
+ val clockSizeRadioGroup =
+ view.requireViewById<RadioGroup>(R.id.clock_size_radio_button_group)
+ clockSizeRadioGroup.setOnCheckedChangeListener(onCheckedChangeListener)
+ view.requireViewById<RadioButton>(R.id.radio_dynamic).text =
+ getRadioText(
+ view.context.applicationContext,
+ view.resources.getString(R.string.clock_size_dynamic),
+ view.resources.getString(R.string.clock_size_dynamic_description)
+ )
+ view.requireViewById<RadioButton>(R.id.radio_small).text =
+ getRadioText(
+ view.context.applicationContext,
+ view.resources.getString(R.string.clock_size_small),
+ view.resources.getString(R.string.clock_size_small_description)
+ )
val colorOptionContainer = view.requireViewById<View>(R.id.color_picker_container)
lifecycleOwner.lifecycleScope.launch {
@@ -110,11 +129,11 @@ object ClockSettingsBinder {
when (tab) {
ClockSettingsViewModel.Tab.COLOR -> {
colorOptionContainer.isVisible = true
- sizeOptions.isInvisible = true
+ clockSizeRadioGroup.isInvisible = true
}
ClockSettingsViewModel.Tab.SIZE -> {
colorOptionContainer.isInvisible = true
- sizeOptions.isVisible = true
+ clockSizeRadioGroup.isVisible = true
}
}
}
@@ -122,6 +141,7 @@ object ClockSettingsBinder {
launch {
viewModel.colorOptions.collect { colorOptions ->
+ colorOptionContainerListView.removeAllViews()
colorOptions.forEachIndexed { index, colorOption ->
colorOption.payload?.let { payload ->
val item =
@@ -189,16 +209,28 @@ object ClockSettingsBinder {
clockHostView.addView(clockView)
when (size) {
ClockSize.DYNAMIC -> {
- sizeOptions.radioButtonDynamic.isChecked = true
- sizeOptions.radioButtonSmall.isChecked = false
+ // When clock size data flow emits clock size signal, we want
+ // to update the view without triggering on checked change,
+ // which is supposed to be triggered by user interaction only.
+ clockSizeRadioGroup.setOnCheckedChangeListener(null)
+ clockSizeRadioGroup.check(R.id.radio_dynamic)
+ clockSizeRadioGroup.setOnCheckedChangeListener(
+ onCheckedChangeListener
+ )
clockHostView.doOnPreDraw {
it.pivotX = it.width / 2F
it.pivotY = it.height / 2F
}
}
ClockSize.SMALL -> {
- sizeOptions.radioButtonDynamic.isChecked = false
- sizeOptions.radioButtonSmall.isChecked = true
+ // When clock size data flow emits clock size signal, we want
+ // to update the view without triggering on checked change,
+ // which is supposed to be triggered by user interaction only.
+ clockSizeRadioGroup.setOnCheckedChangeListener(null)
+ clockSizeRadioGroup.check(R.id.radio_small)
+ clockSizeRadioGroup.setOnCheckedChangeListener(
+ onCheckedChangeListener
+ )
clockHostView.doOnPreDraw {
it.pivotX = ClockCarouselView.getCenteredHostViewPivotX(it)
it.pivotY = 0F
@@ -238,4 +270,25 @@ object ClockSettingsBinder {
}
)
}
+
+ private fun getRadioText(
+ context: Context,
+ title: String,
+ description: String
+ ): SpannableString {
+ val text = SpannableString(title + "\n" + description)
+ text.setSpan(
+ TextAppearanceSpan(context, R.style.SectionTitleTextStyle),
+ 0,
+ title.length,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ text.setSpan(
+ TextAppearanceSpan(context, R.style.SectionSubtitleTextStyle),
+ title.length + 1,
+ title.length + 1 + description.length,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ return text
+ }
}
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 4805f376..dc70633e 100644
--- a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
+++ b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
@@ -68,7 +68,7 @@ class ClockSettingsFragment : AppbarFragment() {
val injector = InjectorProvider.getInjector() as ThemePickerInjector
val lockScreenView: CardView = view.requireViewById(R.id.lock_preview)
- val colorViewModel = injector.getWallpaperColorsViewModel()
+ val wallpaperColorsRepository = injector.getWallpaperColorsRepository()
val displayUtils = injector.getDisplayUtils(context)
ScreenPreviewBinder.bind(
activity = activity,
@@ -88,18 +88,18 @@ class ClockSettingsFragment : AppbarFragment() {
injector
.getCurrentWallpaperInfoFactory(context)
.createCurrentWallpaperInfos(
- { homeWallpaper, lockWallpaper, _ ->
- continuation.resume(
- lockWallpaper ?: homeWallpaper,
- null,
- )
- },
+ context,
forceReload,
- )
+ ) { homeWallpaper, lockWallpaper, _ ->
+ continuation.resume(
+ lockWallpaper ?: homeWallpaper,
+ null,
+ )
+ }
}
},
onWallpaperColorChanged = { colors ->
- colorViewModel.setLockWallpaperColors(colors)
+ wallpaperColorsRepository.setLockWallpaperColors(colors)
},
initialExtrasProvider = {
Bundle().apply {
@@ -125,7 +125,7 @@ class ClockSettingsFragment : AppbarFragment() {
this,
injector.getClockSettingsViewModelFactory(
context,
- injector.getWallpaperColorsViewModel(),
+ injector.getWallpaperColorsRepository(),
injector.getClockViewFactory(activity),
),
)
diff --git a/src/com/android/customization/picker/clock/ui/section/ClockSectionController.kt b/src/com/android/customization/picker/clock/ui/section/ClockSectionController.kt
deleted file mode 100644
index b47c2433..00000000
--- a/src/com/android/customization/picker/clock/ui/section/ClockSectionController.kt
+++ /dev/null
@@ -1,62 +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.clock.ui.section
-
-import android.content.Context
-import android.view.LayoutInflater
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import com.android.customization.picker.clock.ui.binder.ClockSectionViewBinder
-import com.android.customization.picker.clock.ui.fragment.ClockSettingsFragment
-import com.android.customization.picker.clock.ui.view.ClockSectionView
-import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
-import com.android.wallpaper.R
-import com.android.wallpaper.config.BaseFlags
-import com.android.wallpaper.model.CustomizationSectionController
-import com.android.wallpaper.model.CustomizationSectionController.CustomizationSectionNavigationController
-import kotlinx.coroutines.launch
-
-/** A [CustomizationSectionController] for clock customization. */
-class ClockSectionController(
- private val navigationController: CustomizationSectionNavigationController,
- private val lifecycleOwner: LifecycleOwner,
- private val flag: BaseFlags,
- private val viewModel: ClockSectionViewModel,
-) : CustomizationSectionController<ClockSectionView> {
-
- override fun isAvailable(context: Context): Boolean {
- return flag.isCustomClocksEnabled(context!!)
- }
-
- override fun createView(context: Context): ClockSectionView {
- val view =
- LayoutInflater.from(context)
- .inflate(
- R.layout.clock_section_view,
- null,
- ) as ClockSectionView
- lifecycleOwner.lifecycleScope.launch {
- ClockSectionViewBinder.bind(
- view = view,
- viewModel = viewModel,
- lifecycleOwner = lifecycleOwner
- ) {
- navigationController.navigateTo(ClockSettingsFragment())
- }
- }
- return view
- }
-}
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 d4f501b7..cae4e06b 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -31,7 +31,7 @@ import androidx.core.view.get
import androidx.core.view.isNotEmpty
import com.android.customization.picker.clock.shared.ClockSize
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselItemViewModel
-import com.android.systemui.plugins.ClockController
+import com.android.systemui.plugins.clocks.ClockController
import com.android.wallpaper.R
import com.android.wallpaper.picker.FixedWidthDisplayRatioFrameLayout
import java.lang.Float.max
@@ -384,7 +384,7 @@ class ClockCarouselView(
) : Carousel.Adapter {
fun getContentDescription(index: Int, resources: Resources): String {
- return clocks[index].getContentDescription(resources)
+ return clocks[index].contentDescription
}
override fun count(): Int {
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockSizeRadioButtonGroup.kt b/src/com/android/customization/picker/clock/ui/view/ClockSizeRadioButtonGroup.kt
deleted file mode 100644
index 909491a3..00000000
--- a/src/com/android/customization/picker/clock/ui/view/ClockSizeRadioButtonGroup.kt
+++ /dev/null
@@ -1,50 +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.clock.ui.view
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.FrameLayout
-import android.widget.RadioButton
-import com.android.customization.picker.clock.shared.ClockSize
-import com.android.wallpaper.R
-
-/** The radio button group to pick the clock size. */
-class ClockSizeRadioButtonGroup(
- context: Context,
- attrs: AttributeSet?,
-) : FrameLayout(context, attrs) {
-
- interface OnRadioButtonClickListener {
- fun onClick(size: ClockSize)
- }
-
- val radioButtonDynamic: RadioButton
- val radioButtonSmall: RadioButton
- var onRadioButtonClickListener: OnRadioButtonClickListener? = null
-
- init {
- LayoutInflater.from(context).inflate(R.layout.clock_size_radio_button_group, this, true)
- radioButtonDynamic = requireViewById(R.id.radio_button_dynamic)
- val buttonDynamic = requireViewById<View>(R.id.button_container_dynamic)
- buttonDynamic.setOnClickListener { onRadioButtonClickListener?.onClick(ClockSize.DYNAMIC) }
- radioButtonSmall = requireViewById(R.id.radio_button_large)
- val buttonLarge = requireViewById<View>(R.id.button_container_small)
- buttonLarge.setOnClickListener { onRadioButtonClickListener?.onClick(ClockSize.SMALL) }
- }
-}
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
index 3f6f423f..2ab162d3 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
@@ -15,226 +15,38 @@
*/
package com.android.customization.picker.clock.ui.view
-import android.app.WallpaperColors
-import android.app.WallpaperManager
-import android.content.Context
-import android.content.res.Resources
-import android.graphics.Point
-import android.graphics.Rect
import android.view.View
-import android.widget.FrameLayout
import androidx.annotation.ColorInt
-import androidx.core.text.util.LocalePreferences
import androidx.lifecycle.LifecycleOwner
-import com.android.systemui.plugins.ClockController
-import com.android.systemui.plugins.WeatherData
-import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.wallpaper.R
-import com.android.wallpaper.util.TimeUtils.TimeTicker
-import java.util.concurrent.ConcurrentHashMap
+import com.android.systemui.plugins.clocks.ClockController
-/**
- * Provide reusable clock view and related util functions.
- *
- * @property screenSize The Activity or Fragment's window size.
- */
-class ClockViewFactory(
- private val appContext: Context,
- val screenSize: Point,
- private val wallpaperManager: WallpaperManager,
- private val registry: ClockRegistry,
-) {
- private val resources = appContext.resources
- private val timeTickListeners: ConcurrentHashMap<Int, TimeTicker> = ConcurrentHashMap()
- private val clockControllers: HashMap<String, ClockController> = HashMap()
- private val smallClockFrames: HashMap<String, FrameLayout> = HashMap()
+interface ClockViewFactory {
- fun getController(clockId: String): ClockController {
- return clockControllers[clockId]
- ?: initClockController(clockId).also { clockControllers[clockId] = it }
- }
+ fun getController(clockId: String): ClockController
/**
* Reset the large view to its initial state when getting the view. This is because some view
* configs, e.g. animation state, might change during the reuse of the clock view in the app.
*/
- fun getLargeView(clockId: String): View {
- return getController(clockId).largeClock.let {
- it.animations.onPickerCarouselSwiping(1F)
- it.view
- }
- }
+ fun getLargeView(clockId: String): View
/**
* Reset the small view to its initial state when getting the view. This is because some view
* configs, e.g. translation X, might change during the reuse of the clock view in the app.
*/
- fun getSmallView(clockId: String): View {
- val smallClockFrame =
- smallClockFrames[clockId]
- ?: createSmallClockFrame().also {
- it.addView(getController(clockId).smallClock.view)
- smallClockFrames[clockId] = it
- }
- smallClockFrame.translationX = 0F
- smallClockFrame.translationY = 0F
- return smallClockFrame
- }
-
- private fun createSmallClockFrame(): FrameLayout {
- val smallClockFrame = FrameLayout(appContext)
- val layoutParams =
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- resources.getDimensionPixelSize(R.dimen.small_clock_height)
- )
- layoutParams.topMargin = getSmallClockTopMargin()
- layoutParams.marginStart = getSmallClockStartPadding()
- smallClockFrame.layoutParams = layoutParams
- smallClockFrame.clipChildren = false
- return smallClockFrame
- }
-
- private fun getSmallClockTopMargin() =
- getStatusBarHeight(appContext.resources) +
- appContext.resources.getDimensionPixelSize(R.dimen.small_clock_padding_top)
-
- private fun getSmallClockStartPadding() =
- appContext.resources.getDimensionPixelSize(R.dimen.clock_padding_start)
-
- fun updateColorForAllClocks(@ColorInt seedColor: Int?) {
- clockControllers.values.forEach { it.events.onSeedColorChanged(seedColor = seedColor) }
- }
-
- fun updateColor(clockId: String, @ColorInt seedColor: Int?) {
- clockControllers[clockId]?.events?.onSeedColorChanged(seedColor)
- }
-
- fun updateRegionDarkness() {
- val isRegionDark = isLockscreenWallpaperDark()
- clockControllers.values.forEach {
- it.largeClock.events.onRegionDarknessChanged(isRegionDark)
- it.smallClock.events.onRegionDarknessChanged(isRegionDark)
- }
- }
-
- private fun isLockscreenWallpaperDark(): Boolean {
- val colors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK)
- return (colors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) == 0
- }
-
- fun updateTimeFormat(clockId: String) {
- getController(clockId)
- .events
- .onTimeFormatChanged(android.text.format.DateFormat.is24HourFormat(appContext))
- }
+ fun getSmallView(clockId: String): View
- fun registerTimeTicker(owner: LifecycleOwner) {
- val hashCode = owner.hashCode()
- if (timeTickListeners.keys.contains(hashCode)) {
- return
- }
+ fun updateColorForAllClocks(@ColorInt seedColor: Int?)
- timeTickListeners[hashCode] = TimeTicker.registerNewReceiver(appContext) { onTimeTick() }
- }
+ fun updateColor(clockId: String, @ColorInt seedColor: Int?)
- fun onDestroy() {
- timeTickListeners.forEach { (_, timeTicker) -> appContext.unregisterReceiver(timeTicker) }
- timeTickListeners.clear()
- clockControllers.clear()
- smallClockFrames.clear()
- }
+ fun updateRegionDarkness()
- private fun onTimeTick() {
- clockControllers.values.forEach {
- it.largeClock.events.onTimeTick()
- it.smallClock.events.onTimeTick()
- }
- }
+ fun updateTimeFormat(clockId: String)
- fun unregisterTimeTicker(owner: LifecycleOwner) {
- val hashCode = owner.hashCode()
- timeTickListeners[hashCode]?.let {
- appContext.unregisterReceiver(it)
- timeTickListeners.remove(hashCode)
- }
- }
-
- private fun initClockController(clockId: String): ClockController {
- val controller =
- registry.createExampleClock(clockId).also { it?.initialize(resources, 0f, 0f) }
- checkNotNull(controller)
-
- val isWallpaperDark = isLockscreenWallpaperDark()
- // Initialize large clock
- controller.largeClock.events.onRegionDarknessChanged(isWallpaperDark)
- controller.largeClock.events.onFontSettingChanged(
- resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
- )
- controller.largeClock.events.onTargetRegionChanged(getLargeClockRegion())
-
- // Initialize small clock
- controller.smallClock.events.onRegionDarknessChanged(isWallpaperDark)
- controller.smallClock.events.onFontSettingChanged(
- resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
- )
- controller.smallClock.events.onTargetRegionChanged(getSmallClockRegion())
-
- // Use placeholder for weather clock preview in picker.
- // Use locale default temp unit since assistant default is not available in this context.
- val useCelsius =
- LocalePreferences.getTemperatureUnit() == LocalePreferences.TemperatureUnit.CELSIUS
- controller.events.onWeatherDataChanged(
- WeatherData(
- description = DESCRIPTION_PLACEHODLER,
- state = WEATHERICON_PLACEHOLDER,
- temperature =
- if (useCelsius) TEMPERATURE_CELSIUS_PLACEHOLDER
- else TEMPERATURE_FAHRENHEIT_PLACEHOLDER,
- useCelsius = useCelsius,
- )
- )
- return controller
- }
-
- /**
- * Simulate the function of getLargeClockRegion in KeyguardClockSwitch so that we can get a
- * proper region corresponding to lock screen in picker and for onTargetRegionChanged to scale
- * and position the clock view
- */
- private fun getLargeClockRegion(): Rect {
- val largeClockTopMargin =
- resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)
- val targetHeight = resources.getDimensionPixelSize(R.dimen.large_clock_text_size) * 2
- val top = (screenSize.y / 2 - targetHeight / 2 + largeClockTopMargin / 2)
- return Rect(0, top, screenSize.x, (top + targetHeight))
- }
-
- /**
- * Simulate the function of getSmallClockRegion in KeyguardClockSwitch so that we can get a
- * proper region corresponding to lock screen in picker and for onTargetRegionChanged to scale
- * and position the clock view
- */
- private fun getSmallClockRegion(): Rect {
- val topMargin = getSmallClockTopMargin()
- val targetHeight = resources.getDimensionPixelSize(R.dimen.small_clock_height)
- return Rect(getSmallClockStartPadding(), topMargin, screenSize.x, topMargin + targetHeight)
- }
+ fun registerTimeTicker(owner: LifecycleOwner)
- companion object {
- const val DESCRIPTION_PLACEHODLER = ""
- const val TEMPERATURE_FAHRENHEIT_PLACEHOLDER = 58
- const val TEMPERATURE_CELSIUS_PLACEHOLDER = 21
- val WEATHERICON_PLACEHOLDER = WeatherData.WeatherStateIcon.MOSTLY_SUNNY
- const val USE_CELSIUS_PLACEHODLER = false
+ fun onDestroy()
- private fun getStatusBarHeight(resource: Resources): Int {
- var result = 0
- val resourceId: Int = resource.getIdentifier("status_bar_height", "dimen", "android")
- if (resourceId > 0) {
- result = resource.getDimensionPixelSize(resourceId)
- }
- return result
- }
- }
+ fun unregisterTimeTicker(owner: LifecycleOwner)
}
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockViewFactoryImpl.kt b/src/com/android/customization/picker/clock/ui/view/ClockViewFactoryImpl.kt
new file mode 100644
index 00000000..5caea58d
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/view/ClockViewFactoryImpl.kt
@@ -0,0 +1,240 @@
+/*
+ * 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.clock.ui.view
+
+import android.app.WallpaperColors
+import android.app.WallpaperManager
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Point
+import android.graphics.Rect
+import android.view.View
+import android.widget.FrameLayout
+import androidx.annotation.ColorInt
+import androidx.core.text.util.LocalePreferences
+import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.WeatherData
+import com.android.systemui.shared.clocks.ClockRegistry
+import com.android.wallpaper.R
+import com.android.wallpaper.util.TimeUtils.TimeTicker
+import java.util.concurrent.ConcurrentHashMap
+
+/**
+ * Provide reusable clock view and related util functions.
+ *
+ * @property screenSize The Activity or Fragment's window size.
+ */
+class ClockViewFactoryImpl(
+ private val appContext: Context,
+ val screenSize: Point,
+ private val wallpaperManager: WallpaperManager,
+ private val registry: ClockRegistry,
+) : ClockViewFactory {
+ private val resources = appContext.resources
+ private val timeTickListeners: ConcurrentHashMap<Int, TimeTicker> = ConcurrentHashMap()
+ private val clockControllers: HashMap<String, ClockController> = HashMap()
+ private val smallClockFrames: HashMap<String, FrameLayout> = HashMap()
+
+ override fun getController(clockId: String): ClockController {
+ return clockControllers[clockId]
+ ?: initClockController(clockId).also { clockControllers[clockId] = it }
+ }
+
+ /**
+ * Reset the large view to its initial state when getting the view. This is because some view
+ * configs, e.g. animation state, might change during the reuse of the clock view in the app.
+ */
+ override fun getLargeView(clockId: String): View {
+ return getController(clockId).largeClock.let {
+ it.animations.onPickerCarouselSwiping(1F)
+ it.view
+ }
+ }
+
+ /**
+ * Reset the small view to its initial state when getting the view. This is because some view
+ * configs, e.g. translation X, might change during the reuse of the clock view in the app.
+ */
+ override fun getSmallView(clockId: String): View {
+ val smallClockFrame =
+ smallClockFrames[clockId]
+ ?: createSmallClockFrame().also {
+ it.addView(getController(clockId).smallClock.view)
+ smallClockFrames[clockId] = it
+ }
+ smallClockFrame.translationX = 0F
+ smallClockFrame.translationY = 0F
+ return smallClockFrame
+ }
+
+ private fun createSmallClockFrame(): FrameLayout {
+ val smallClockFrame = FrameLayout(appContext)
+ val layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ resources.getDimensionPixelSize(R.dimen.small_clock_height)
+ )
+ layoutParams.topMargin = getSmallClockTopMargin()
+ layoutParams.marginStart = getSmallClockStartPadding()
+ smallClockFrame.layoutParams = layoutParams
+ smallClockFrame.clipChildren = false
+ return smallClockFrame
+ }
+
+ private fun getSmallClockTopMargin() =
+ getStatusBarHeight(appContext.resources) +
+ appContext.resources.getDimensionPixelSize(R.dimen.small_clock_padding_top)
+
+ private fun getSmallClockStartPadding() =
+ appContext.resources.getDimensionPixelSize(R.dimen.clock_padding_start)
+
+ override fun updateColorForAllClocks(@ColorInt seedColor: Int?) {
+ clockControllers.values.forEach { it.events.onSeedColorChanged(seedColor = seedColor) }
+ }
+
+ override fun updateColor(clockId: String, @ColorInt seedColor: Int?) {
+ clockControllers[clockId]?.events?.onSeedColorChanged(seedColor)
+ }
+
+ override fun updateRegionDarkness() {
+ val isRegionDark = isLockscreenWallpaperDark()
+ clockControllers.values.forEach {
+ it.largeClock.events.onRegionDarknessChanged(isRegionDark)
+ it.smallClock.events.onRegionDarknessChanged(isRegionDark)
+ }
+ }
+
+ private fun isLockscreenWallpaperDark(): Boolean {
+ val colors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK)
+ return (colors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) == 0
+ }
+
+ override fun updateTimeFormat(clockId: String) {
+ getController(clockId)
+ .events
+ .onTimeFormatChanged(android.text.format.DateFormat.is24HourFormat(appContext))
+ }
+
+ override fun registerTimeTicker(owner: LifecycleOwner) {
+ val hashCode = owner.hashCode()
+ if (timeTickListeners.keys.contains(hashCode)) {
+ return
+ }
+
+ timeTickListeners[hashCode] = TimeTicker.registerNewReceiver(appContext) { onTimeTick() }
+ }
+
+ override fun onDestroy() {
+ timeTickListeners.forEach { (_, timeTicker) -> appContext.unregisterReceiver(timeTicker) }
+ timeTickListeners.clear()
+ clockControllers.clear()
+ smallClockFrames.clear()
+ }
+
+ private fun onTimeTick() {
+ clockControllers.values.forEach {
+ it.largeClock.events.onTimeTick()
+ it.smallClock.events.onTimeTick()
+ }
+ }
+
+ override fun unregisterTimeTicker(owner: LifecycleOwner) {
+ val hashCode = owner.hashCode()
+ timeTickListeners[hashCode]?.let {
+ appContext.unregisterReceiver(it)
+ timeTickListeners.remove(hashCode)
+ }
+ }
+
+ private fun initClockController(clockId: String): ClockController {
+ val controller =
+ registry.createExampleClock(clockId).also { it?.initialize(resources, 0f, 0f) }
+ checkNotNull(controller)
+
+ val isWallpaperDark = isLockscreenWallpaperDark()
+ // Initialize large clock
+ controller.largeClock.events.onRegionDarknessChanged(isWallpaperDark)
+ controller.largeClock.events.onFontSettingChanged(
+ resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
+ )
+ controller.largeClock.events.onTargetRegionChanged(getLargeClockRegion())
+
+ // Initialize small clock
+ controller.smallClock.events.onRegionDarknessChanged(isWallpaperDark)
+ controller.smallClock.events.onFontSettingChanged(
+ resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+ )
+ controller.smallClock.events.onTargetRegionChanged(getSmallClockRegion())
+
+ // Use placeholder for weather clock preview in picker.
+ // Use locale default temp unit since assistant default is not available in this context.
+ val useCelsius =
+ LocalePreferences.getTemperatureUnit() == LocalePreferences.TemperatureUnit.CELSIUS
+ controller.events.onWeatherDataChanged(
+ WeatherData(
+ description = DESCRIPTION_PLACEHODLER,
+ state = WEATHERICON_PLACEHOLDER,
+ temperature =
+ if (useCelsius) TEMPERATURE_CELSIUS_PLACEHOLDER
+ else TEMPERATURE_FAHRENHEIT_PLACEHOLDER,
+ useCelsius = useCelsius,
+ )
+ )
+ return controller
+ }
+
+ /**
+ * Simulate the function of getLargeClockRegion in KeyguardClockSwitch so that we can get a
+ * proper region corresponding to lock screen in picker and for onTargetRegionChanged to scale
+ * and position the clock view
+ */
+ private fun getLargeClockRegion(): Rect {
+ val largeClockTopMargin =
+ resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)
+ val targetHeight = resources.getDimensionPixelSize(R.dimen.large_clock_text_size) * 2
+ val top = (screenSize.y / 2 - targetHeight / 2 + largeClockTopMargin / 2)
+ return Rect(0, top, screenSize.x, (top + targetHeight))
+ }
+
+ /**
+ * Simulate the function of getSmallClockRegion in KeyguardClockSwitch so that we can get a
+ * proper region corresponding to lock screen in picker and for onTargetRegionChanged to scale
+ * and position the clock view
+ */
+ private fun getSmallClockRegion(): Rect {
+ val topMargin = getSmallClockTopMargin()
+ val targetHeight = resources.getDimensionPixelSize(R.dimen.small_clock_height)
+ return Rect(getSmallClockStartPadding(), topMargin, screenSize.x, topMargin + targetHeight)
+ }
+
+ companion object {
+ const val DESCRIPTION_PLACEHODLER = ""
+ const val TEMPERATURE_FAHRENHEIT_PLACEHOLDER = 58
+ const val TEMPERATURE_CELSIUS_PLACEHOLDER = 21
+ val WEATHERICON_PLACEHOLDER = WeatherData.WeatherStateIcon.MOSTLY_SUNNY
+ const val USE_CELSIUS_PLACEHODLER = false
+
+ private fun getStatusBarHeight(resource: Resources): Int {
+ var result = 0
+ val resourceId: Int = resource.getIdentifier("status_bar_height", "dimen", "android")
+ if (resourceId > 0) {
+ result = resource.getDimensionPixelSize(resourceId)
+ }
+ return result
+ }
+ }
+}
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 98114260..e5ac953c 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt
@@ -15,20 +15,8 @@
*/
package com.android.customization.picker.clock.ui.viewmodel
-import android.content.res.Resources
-import com.android.customization.module.CustomizationInjector
-import com.android.wallpaper.R
-import com.android.wallpaper.module.InjectorProvider
-
-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(resources)
- ?.getDescription(clockId)
- ?: ""
- return resources.getString(R.string.select_clock_action_description, clockContent)
- }
-}
+class ClockCarouselItemViewModel(
+ val clockId: String,
+ val isSelected: Boolean,
+ val contentDescription: String,
+)
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 27c25a20..3f6394be 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
@@ -15,12 +15,15 @@
*/
package com.android.customization.picker.clock.ui.viewmodel
+import android.content.res.Resources
import android.graphics.Color
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
+import com.android.customization.module.logging.ThemesUserEventLogger
import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
import com.android.customization.picker.clock.shared.ClockSize
+import com.android.customization.picker.clock.ui.view.ClockViewFactory
import com.android.wallpaper.R
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -43,6 +46,9 @@ import kotlinx.coroutines.launch
class ClockCarouselViewModel(
private val interactor: ClockPickerInteractor,
private val backgroundDispatcher: CoroutineDispatcher,
+ private val clockViewFactory: ClockViewFactory,
+ private val resources: Resources,
+ private val logger: ThemesUserEventLogger,
) : ViewModel() {
@OptIn(ExperimentalCoroutinesApi::class)
val allClocks: StateFlow<List<ClockCarouselItemViewModel>> =
@@ -50,7 +56,14 @@ class ClockCarouselViewModel(
.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, it.isSelected) }
+ allClocks.map {
+ val contentDescription =
+ resources.getString(
+ R.string.select_clock_action_description,
+ clockViewFactory.getController(it.clockId).config.description
+ )
+ ClockCarouselItemViewModel(it.clockId, it.isSelected, contentDescription)
+ }
}
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
@@ -111,18 +124,27 @@ class ClockCarouselViewModel(
fun setSelectedClock(clockId: String) {
setSelectedClockJob?.cancel()
setSelectedClockJob =
- viewModelScope.launch(backgroundDispatcher) { interactor.setSelectedClock(clockId) }
+ viewModelScope.launch(backgroundDispatcher) {
+ interactor.setSelectedClock(clockId)
+ logger.logClockApplied(clockId)
+ }
}
class Factory(
private val interactor: ClockPickerInteractor,
private val backgroundDispatcher: CoroutineDispatcher,
+ private val clockViewFactory: ClockViewFactory,
+ private val resources: Resources,
+ private val logger: ThemesUserEventLogger,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return ClockCarouselViewModel(
interactor = interactor,
backgroundDispatcher = backgroundDispatcher,
+ clockViewFactory = clockViewFactory,
+ resources = resources,
+ logger = logger,
)
as T
}
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt
deleted file mode 100644
index 8a655225..00000000
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt
+++ /dev/null
@@ -1,51 +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.clock.ui.viewmodel
-
-import android.content.Context
-import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
-import com.android.customization.picker.clock.shared.ClockSize
-import com.android.wallpaper.R
-import java.util.Locale
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
-
-/** View model for the clock section view on the lockscreen customization surface. */
-class ClockSectionViewModel(context: Context, interactor: ClockPickerInteractor) {
- val appContext: Context = context.applicationContext
- val clockColorMap: Map<String, ClockColorViewModel> =
- ClockColorViewModel.getPresetColorMap(appContext.resources)
- val selectedClockColorAndSizeText: Flow<String> =
- combine(interactor.selectedColorId, interactor.selectedClockSize, ::Pair).map {
- (selectedColorId, selectedClockSize) ->
- val colorText =
- clockColorMap[selectedColorId]?.colorName
- ?: appContext.getString(R.string.default_theme_title)
- val sizeText =
- when (selectedClockSize) {
- ClockSize.SMALL -> appContext.getString(R.string.clock_size_small)
- ClockSize.DYNAMIC -> appContext.getString(R.string.clock_size_dynamic)
- }
- appContext
- .getString(R.string.clock_color_and_size_description, colorText, sizeText)
- .lowercase()
- .replaceFirstChar {
- if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
- }
- }
-}
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
index a498c716..d0e4f8fe 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
@@ -21,9 +21,12 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.customization.model.color.ColorOptionImpl
+import com.android.customization.module.logging.ThemesUserEventLogger
+import com.android.customization.module.logging.ThemesUserEventLogger.Companion.NULL_SEED_COLOR
import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
import com.android.customization.picker.clock.shared.ClockSize
import com.android.customization.picker.clock.shared.model.ClockMetadataModel
+import com.android.customization.picker.clock.shared.toClockSizeForLogging
import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
import com.android.customization.picker.color.shared.model.ColorOptionModel
import com.android.customization.picker.color.shared.model.ColorType
@@ -53,6 +56,7 @@ private constructor(
private val clockPickerInteractor: ClockPickerInteractor,
private val colorPickerInteractor: ColorPickerInteractor,
private val getIsReactiveToTone: (clockId: String?) -> Boolean,
+ private val logger: ThemesUserEventLogger,
) : ViewModel() {
enum class Tab {
@@ -106,15 +110,17 @@ private constructor(
suspend fun onSliderProgressStop(progress: Int) {
val selectedColorId = selectedColorId.value ?: return
val clockColorViewModel = colorMap[selectedColorId] ?: return
+ val seedColor =
+ blendColorWithTone(
+ color = clockColorViewModel.color,
+ colorTone = clockColorViewModel.getColorTone(progress),
+ )
clockPickerInteractor.setClockColor(
selectedColorId = selectedColorId,
colorToneProgress = progress,
- seedColor =
- blendColorWithTone(
- color = clockColorViewModel.color,
- colorTone = clockColorViewModel.getColorTone(progress),
- )
+ seedColor = seedColor,
)
+ logger.logClockColorApplied(seedColor)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -169,18 +175,20 @@ private constructor(
} else {
{
viewModelScope.launch {
+ val seedColor =
+ blendColorWithTone(
+ color = colorModel.color,
+ colorTone =
+ colorModel.getColorTone(
+ colorToneProgress,
+ ),
+ )
clockPickerInteractor.setClockColor(
selectedColorId = colorModel.colorId,
colorToneProgress = colorToneProgress,
- seedColor =
- blendColorWithTone(
- color = colorModel.color,
- colorTone =
- colorModel.getColorTone(
- colorToneProgress,
- ),
- ),
+ seedColor = seedColor,
)
+ logger.logClockColorApplied(seedColor)
}
}
}
@@ -244,6 +252,7 @@ private constructor(
ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS,
seedColor = null,
)
+ logger.logClockColorApplied(NULL_SEED_COLOR)
}
}
}
@@ -254,7 +263,10 @@ private constructor(
val selectedClockSize: Flow<ClockSize> = clockPickerInteractor.selectedClockSize
fun setClockSize(size: ClockSize) {
- viewModelScope.launch { clockPickerInteractor.setClockSize(size) }
+ viewModelScope.launch {
+ clockPickerInteractor.setClockSize(size)
+ logger.logClockSizeApplied(size.toClockSizeForLogging())
+ }
}
private val _selectedTabPosition = MutableStateFlow(Tab.COLOR)
@@ -304,6 +316,7 @@ private constructor(
private val context: Context,
private val clockPickerInteractor: ClockPickerInteractor,
private val colorPickerInteractor: ColorPickerInteractor,
+ private val logger: ThemesUserEventLogger,
private val getIsReactiveToTone: (clockId: String?) -> Boolean,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@@ -312,6 +325,7 @@ private constructor(
context = context,
clockPickerInteractor = clockPickerInteractor,
colorPickerInteractor = colorPickerInteractor,
+ logger = logger,
getIsReactiveToTone = getIsReactiveToTone,
)
as T
diff --git a/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt b/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt
deleted file mode 100644
index 28ea4a3f..00000000
--- a/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt
+++ /dev/null
@@ -1,26 +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.clock.utils
-
-/** Provides clock description for accessibility purposes. */
-interface ClockDescriptionUtils {
-
- /**
- * TODO (b/287507746) : Migrate the clock description to system UI or a shared library, instead
- * of preserving at the Wallpaper Picker side.
- */
- 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
deleted file mode 100644
index a04ebfff..00000000
--- a/src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt
+++ /dev/null
@@ -1,22 +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.clock.utils
-
-class ThemePickerClockDescriptionUtils : ClockDescriptionUtils {
- override fun getDescription(clockId: String): String {
- return ""
- }
-}
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 6540ce06..942a8460 100644
--- a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
@@ -24,8 +24,8 @@ import com.android.customization.model.color.ColorOptionImpl
import com.android.customization.picker.color.shared.model.ColorOptionModel
import com.android.customization.picker.color.shared.model.ColorType
import com.android.systemui.monet.Style
-import com.android.wallpaper.model.WallpaperColorsModel
-import com.android.wallpaper.model.WallpaperColorsViewModel
+import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository
+import com.android.wallpaper.picker.customization.shared.model.WallpaperColorsModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -37,14 +37,14 @@ import kotlinx.coroutines.suspendCancellableCoroutine
// TODO (b/262924623): refactor to remove dependency on ColorCustomizationManager & ColorOption
// TODO (b/268203200): Create test for ColorPickerRepositoryImpl
class ColorPickerRepositoryImpl(
- wallpaperColorsViewModel: WallpaperColorsViewModel,
+ wallpaperColorsRepository: WallpaperColorsRepository,
private val colorManager: ColorCustomizationManager,
) : ColorPickerRepository {
private val homeWallpaperColors: StateFlow<WallpaperColorsModel?> =
- wallpaperColorsViewModel.homeWallpaperColors
+ wallpaperColorsRepository.homeWallpaperColors
private val lockWallpaperColors: StateFlow<WallpaperColorsModel?> =
- wallpaperColorsViewModel.lockWallpaperColors
+ wallpaperColorsRepository.lockWallpaperColors
private var selectedColorOption: MutableStateFlow<ColorOptionModel> =
MutableStateFlow(getCurrentColorOption())
@@ -78,7 +78,7 @@ class ColorPickerRepositoryImpl(
homeColorsLoaded.colors,
lockColorsLoaded.colors
)
- colorManager.fetchRevampedUIOptions(
+ colorManager.fetchOptions(
object : CustomizationManager.OptionsFetchedListener<ColorOption?> {
override fun onOptionsLoaded(options: MutableList<ColorOption?>?) {
val wallpaperColorOptions: MutableList<ColorOptionModel> =
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 bb2ef9d3..f35d934d 100644
--- a/src/com/android/customization/picker/color/data/repository/FakeColorPickerRepository.kt
+++ b/src/com/android/customization/picker/color/data/repository/FakeColorPickerRepository.kt
@@ -19,10 +19,12 @@ package com.android.customization.picker.color.data.repository
import android.content.Context
import android.graphics.Color
import android.text.TextUtils
+import com.android.customization.model.ResourceConstants
import com.android.customization.model.color.ColorOptionImpl
import com.android.customization.model.color.ColorOptionsProvider
import com.android.customization.picker.color.shared.model.ColorOptionModel
import com.android.customization.picker.color.shared.model.ColorType
+import com.android.systemui.monet.Style
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -49,6 +51,53 @@ class FakeColorPickerRepository(private val context: Context) : ColorPickerRepos
}
fun setOptions(
+ wallpaperOptions: List<ColorOptionImpl>,
+ presetOptions: List<ColorOptionImpl>,
+ selectedColorOptionType: ColorType,
+ selectedColorOptionIndex: Int
+ ) {
+ _colorOptions.value =
+ mapOf(
+ ColorType.WALLPAPER_COLOR to
+ buildList {
+ for ((index, colorOption) in wallpaperOptions.withIndex()) {
+ val isSelected =
+ selectedColorOptionType == ColorType.WALLPAPER_COLOR &&
+ selectedColorOptionIndex == index
+ val colorOptionModel =
+ ColorOptionModel(
+ key = "${ColorType.WALLPAPER_COLOR}::$index",
+ colorOption = colorOption,
+ isSelected = isSelected
+ )
+ if (isSelected) {
+ selectedColorOption = colorOptionModel
+ }
+ add(colorOptionModel)
+ }
+ },
+ ColorType.PRESET_COLOR to
+ buildList {
+ for ((index, colorOption) in presetOptions.withIndex()) {
+ val isSelected =
+ selectedColorOptionType == ColorType.PRESET_COLOR &&
+ selectedColorOptionIndex == index
+ val colorOptionModel =
+ ColorOptionModel(
+ key = "${ColorType.PRESET_COLOR}::$index",
+ colorOption = colorOption,
+ isSelected = isSelected
+ )
+ if (isSelected) {
+ selectedColorOption = colorOptionModel
+ }
+ add(colorOptionModel)
+ }
+ },
+ )
+ }
+
+ fun setOptions(
numWallpaperOptions: Int,
numPresetOptions: Int,
selectedColorOptionType: ColorType,
@@ -111,6 +160,22 @@ class FakeColorPickerRepository(private val context: Context) : ColorPickerRepos
return builder.build()
}
+ fun buildPresetOption(style: Style, seedColor: String): ColorOptionImpl {
+ val builder = ColorOptionImpl.Builder()
+ builder.lightColors =
+ intArrayOf(Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT)
+ builder.darkColors =
+ intArrayOf(Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT)
+ builder.type = ColorType.PRESET_COLOR
+ builder.source = ColorOptionsProvider.COLOR_SOURCE_PRESET
+ builder.style = style
+ builder.title = "Preset"
+ builder
+ .addOverlayPackage("TEST_PACKAGE_TYPE", "preset_color")
+ .addOverlayPackage(ResourceConstants.OVERLAY_CATEGORY_SYSTEM_PALETTE, seedColor)
+ return builder.build()
+ }
+
private fun buildWallpaperOption(index: Int): ColorOptionImpl {
val builder = ColorOptionImpl.Builder()
builder.lightColors =
@@ -127,6 +192,22 @@ class FakeColorPickerRepository(private val context: Context) : ColorPickerRepos
return builder.build()
}
+ fun buildWallpaperOption(source: String, style: Style, seedColor: String): ColorOptionImpl {
+ val builder = ColorOptionImpl.Builder()
+ builder.lightColors =
+ intArrayOf(Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT)
+ builder.darkColors =
+ intArrayOf(Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT)
+ builder.type = ColorType.WALLPAPER_COLOR
+ builder.source = source
+ builder.style = style
+ builder.title = "Dynamic"
+ builder
+ .addOverlayPackage("TEST_PACKAGE_TYPE", "wallpaper_color")
+ .addOverlayPackage(ResourceConstants.OVERLAY_CATEGORY_SYSTEM_PALETTE, seedColor)
+ return builder.build()
+ }
+
override suspend fun select(colorOptionModel: ColorOptionModel) {
val colorOptions = _colorOptions.value
val wallpaperColorOptions = colorOptions[ColorType.WALLPAPER_COLOR]!!
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 0f82f494..9838c317 100644
--- a/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
+++ b/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
@@ -62,7 +62,7 @@ object ColorPickerBinder {
colorTypeTabView.addItemDecoration(ItemSpacing(ItemSpacing.TAB_ITEM_SPACING_DP))
val colorOptionAdapter =
OptionItemAdapter(
- layoutResourceId = R.layout.color_option_2,
+ layoutResourceId = R.layout.color_option,
lifecycleOwner = lifecycleOwner,
bindIcon = { foregroundView: View, colorIcon: ColorOptionIconViewModel ->
val colorOptionIconView = foregroundView as? ColorOptionIconView
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 4ef29d6e..2c006090 100644
--- a/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt
+++ b/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt
@@ -33,11 +33,11 @@ import com.android.customization.model.mode.DarkModeSectionController
import com.android.customization.module.ThemePickerInjector
import com.android.customization.picker.color.ui.binder.ColorPickerBinder
import com.android.wallpaper.R
-import com.android.wallpaper.model.WallpaperColorsModel
-import com.android.wallpaper.model.WallpaperColorsViewModel
import com.android.wallpaper.module.CustomizationSections
import com.android.wallpaper.module.InjectorProvider
import com.android.wallpaper.picker.AppbarFragment
+import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository
+import com.android.wallpaper.picker.customization.shared.model.WallpaperColorsModel
import com.android.wallpaper.picker.customization.ui.binder.ScreenPreviewBinder
import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel
import com.android.wallpaper.util.DisplayUtils
@@ -76,7 +76,7 @@ class ColorPickerFragment : AppbarFragment() {
val homeScreenView: CardView = view.requireViewById(R.id.home_preview)
val wallpaperInfoFactory = injector.getCurrentWallpaperInfoFactory(requireContext())
val displayUtils: DisplayUtils = injector.getDisplayUtils(requireContext())
- val wcViewModel = injector.getWallpaperColorsViewModel()
+ val wallpaperColorsRepository = injector.getWallpaperColorsRepository()
val wallpaperManager = WallpaperManager.getInstance(requireContext())
binding =
@@ -87,7 +87,7 @@ class ColorPickerFragment : AppbarFragment() {
requireActivity(),
injector.getColorPickerViewModelFactory(
context = requireContext(),
- wallpaperColorsViewModel = wcViewModel,
+ wallpaperColorsRepository = wallpaperColorsRepository,
),
)
.get(),
@@ -114,27 +114,27 @@ class ColorPickerFragment : AppbarFragment() {
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)
- },
+ context,
forceReload,
- )
+ ) { homeWallpaper, lockWallpaper, _ ->
+ lifecycleScope.launch {
+ if (
+ wallpaperColorsRepository.lockWallpaperColors.value
+ is WallpaperColorsModel.Loading
+ ) {
+ loadInitialColors(
+ wallpaperManager,
+ wallpaperColorsRepository,
+ CustomizationSections.Screen.LOCK_SCREEN
+ )
+ }
+ }
+ continuation.resume(lockWallpaper ?: homeWallpaper, null)
+ }
}
},
onWallpaperColorChanged = { colors ->
- wcViewModel.setLockWallpaperColors(colors)
+ wallpaperColorsRepository.setLockWallpaperColors(colors)
},
wallpaperInteractor = injector.getWallpaperInteractor(requireContext()),
screen = CustomizationSections.Screen.LOCK_SCREEN,
@@ -165,27 +165,27 @@ class ColorPickerFragment : AppbarFragment() {
wallpaperInfoProvider = { forceReload ->
suspendCancellableCoroutine { continuation ->
wallpaperInfoFactory.createCurrentWallpaperInfos(
- { homeWallpaper, lockWallpaper, _ ->
- lifecycleScope.launch {
- if (
- wcViewModel.homeWallpaperColors.value
- is WallpaperColorsModel.Loading
- ) {
- loadInitialColors(
- wallpaperManager,
- wcViewModel,
- CustomizationSections.Screen.HOME_SCREEN
- )
- }
- }
- continuation.resume(homeWallpaper ?: lockWallpaper, null)
- },
+ context,
forceReload,
- )
+ ) { homeWallpaper, lockWallpaper, _ ->
+ lifecycleScope.launch {
+ if (
+ wallpaperColorsRepository.homeWallpaperColors.value
+ is WallpaperColorsModel.Loading
+ ) {
+ loadInitialColors(
+ wallpaperManager,
+ wallpaperColorsRepository,
+ CustomizationSections.Screen.HOME_SCREEN
+ )
+ }
+ }
+ continuation.resume(homeWallpaper ?: lockWallpaper, null)
+ }
}
},
onWallpaperColorChanged = { colors ->
- wcViewModel.setHomeWallpaperColors(colors)
+ wallpaperColorsRepository.setHomeWallpaperColors(colors)
},
wallpaperInteractor = injector.getWallpaperInteractor(requireContext()),
screen = CustomizationSections.Screen.HOME_SCREEN,
@@ -202,7 +202,8 @@ class ColorPickerFragment : AppbarFragment() {
DarkModeSectionController(
context,
lifecycle,
- injector.getDarkModeSnapshotRestorer(requireContext())
+ injector.getDarkModeSnapshotRestorer(requireContext()),
+ injector.getUserEventLogger(requireContext()),
)
.createView(requireContext())
darkModeSectionView.background = null
@@ -218,7 +219,7 @@ class ColorPickerFragment : AppbarFragment() {
private suspend fun loadInitialColors(
wallpaperManager: WallpaperManager,
- colorViewModel: WallpaperColorsViewModel,
+ colorViewModel: WallpaperColorsRepository,
screen: CustomizationSections.Screen,
) {
withContext(Dispatchers.IO) {
diff --git a/src/com/android/customization/picker/color/ui/section/ColorSectionController2.kt b/src/com/android/customization/picker/color/ui/section/ColorSectionController.kt
index f1c982b4..a36fd80a 100644
--- a/src/com/android/customization/picker/color/ui/section/ColorSectionController2.kt
+++ b/src/com/android/customization/picker/color/ui/section/ColorSectionController.kt
@@ -22,37 +22,37 @@ import android.view.LayoutInflater
import androidx.lifecycle.LifecycleOwner
import com.android.customization.picker.color.ui.binder.ColorSectionViewBinder
import com.android.customization.picker.color.ui.fragment.ColorPickerFragment
-import com.android.customization.picker.color.ui.view.ColorSectionView2
+import com.android.customization.picker.color.ui.view.ColorSectionView
import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel
import com.android.wallpaper.R
import com.android.wallpaper.model.CustomizationSectionController
import com.android.wallpaper.model.CustomizationSectionController.CustomizationSectionNavigationController as NavigationController
-class ColorSectionController2(
+class ColorSectionController(
private val navigationController: NavigationController,
private val viewModel: ColorPickerViewModel,
private val lifecycleOwner: LifecycleOwner
-) : CustomizationSectionController<ColorSectionView2> {
+) : CustomizationSectionController<ColorSectionView> {
override fun isAvailable(context: Context): Boolean {
return true
}
- override fun createView(context: Context): ColorSectionView2 {
+ override fun createView(context: Context): ColorSectionView {
return createView(context, CustomizationSectionController.ViewCreationParams())
}
override fun createView(
context: Context,
params: CustomizationSectionController.ViewCreationParams
- ): ColorSectionView2 {
+ ): ColorSectionView {
@SuppressWarnings("It is fine to inflate with null parent for our need.")
val view =
LayoutInflater.from(context)
.inflate(
- R.layout.color_section_view2,
+ R.layout.color_section_view,
null,
- ) as ColorSectionView2
+ ) as ColorSectionView
ColorSectionViewBinder.bind(
view = view,
viewModel = viewModel,
diff --git a/src/com/android/customization/picker/color/ui/view/ColorSectionView2.kt b/src/com/android/customization/picker/color/ui/view/ColorSectionView.kt
index 7a8f21af..a89292d8 100644
--- a/src/com/android/customization/picker/color/ui/view/ColorSectionView2.kt
+++ b/src/com/android/customization/picker/color/ui/view/ColorSectionView.kt
@@ -23,4 +23,4 @@ import com.android.wallpaper.picker.SectionView
* The class inherits from {@link SectionView} as the view representing the color section of the
* customization picker. It displays a list of color options and an overflow option.
*/
-class ColorSectionView2(context: Context, attrs: AttributeSet?) : SectionView(context, attrs)
+class ColorSectionView(context: Context, attrs: AttributeSet?) : SectionView(context, attrs)
diff --git a/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt b/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt
index 67c68387..ed83136e 100644
--- a/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt
+++ b/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt
@@ -21,6 +21,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.customization.model.color.ColorOptionImpl
+import com.android.customization.module.logging.ThemesUserEventLogger
import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
import com.android.customization.picker.color.shared.model.ColorType
import com.android.wallpaper.R
@@ -43,6 +44,7 @@ class ColorPickerViewModel
private constructor(
context: Context,
private val interactor: ColorPickerInteractor,
+ private val logger: ThemesUserEventLogger,
) : ViewModel() {
private val selectedColorTypeTabId = MutableStateFlow<ColorType?>(null)
@@ -142,6 +144,14 @@ private constructor(
{
viewModelScope.launch {
interactor.select(colorOptionModel)
+ logger.logThemeColorApplied(
+ colorOptionModel.colorOption
+ .sourceForLogging,
+ colorOptionModel.colorOption
+ .styleForLogging,
+ colorOptionModel.colorOption
+ .seedColorForLogging,
+ )
}
}
}
@@ -205,12 +215,14 @@ private constructor(
class Factory(
private val context: Context,
private val interactor: ColorPickerInteractor,
+ private val logger: ThemesUserEventLogger,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return ColorPickerViewModel(
context = context,
interactor = interactor,
+ logger = logger,
)
as T
}
diff --git a/src/com/android/customization/picker/grid/GridFragment.java b/src/com/android/customization/picker/grid/GridFragment.java
deleted file mode 100644
index 4de1dab7..00000000
--- a/src/com/android/customization/picker/grid/GridFragment.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2018 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.grid;
-
-import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY_TEXT;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.Toast;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.constraintlayout.widget.ConstraintSet;
-import androidx.core.widget.ContentLoadingProgressBar;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.customization.model.CustomizationManager.Callback;
-import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
-import com.android.customization.model.CustomizationOption;
-import com.android.customization.model.grid.GridOption;
-import com.android.customization.model.grid.GridOptionViewModel;
-import com.android.customization.model.grid.GridOptionsManager;
-import com.android.customization.module.ThemesUserEventLogger;
-import com.android.customization.picker.WallpaperPreviewer;
-import com.android.customization.widget.OptionSelectorController;
-import com.android.customization.widget.OptionSelectorController.CheckmarkStyle;
-import com.android.wallpaper.R;
-import com.android.wallpaper.model.WallpaperInfo;
-import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
-import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.picker.AppbarFragment;
-import com.android.wallpaper.util.LaunchUtils;
-import com.android.wallpaper.util.ScreenSizeCalculator;
-import com.android.wallpaper.widget.BottomActionBar;
-
-import com.bumptech.glide.Glide;
-
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Fragment that contains the UI for selecting and applying a GridOption.
- */
-public class GridFragment extends AppbarFragment {
-
- private static final String TAG = "GridFragment";
-
- private WallpaperInfo mHomeWallpaper;
- private RecyclerView mOptionsContainer;
- private OptionSelectorController<GridOption> mOptionsController;
- private GridOptionsManager mGridManager;
- private ContentLoadingProgressBar mLoading;
- private ConstraintLayout mContent;
- private View mError;
- private BottomActionBar mBottomActionBar;
- private ThemesUserEventLogger mEventLogger;
- private GridOptionPreviewer mGridOptionPreviewer;
- private GridOptionViewModel mGridOptionViewModel;
-
- private final Callback mApplyGridCallback = new Callback() {
- @Override
- public void onSuccess() {
- mGridManager.fetchOptions(unused -> {}, true);
- Toast.makeText(getContext(), R.string.applied_grid_msg, Toast.LENGTH_SHORT).show();
- getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- getActivity().finish();
-
- // Go back to launcher home
- LaunchUtils.launchHome(getContext());
- }
-
- @Override
- public void onError(@Nullable Throwable throwable) {
- // Since we disabled it when clicked apply button.
- mBottomActionBar.enableActions();
- mBottomActionBar.hide();
- mGridOptionViewModel.setBottomActionBarVisible(false);
- //TODO(chihhangchuang): handle
- }
- };
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mGridOptionViewModel = new ViewModelProvider(requireActivity()).get(
- GridOptionViewModel.class);
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- View view = inflater.inflate(
- R.layout.fragment_grid_picker, container, /* attachToRoot */ false);
- setUpToolbar(view);
- mContent = view.findViewById(R.id.content_section);
- mOptionsContainer = view.findViewById(R.id.options_container);
- AccessibilityManager accessibilityManager =
- (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
- // Make Talkback focus won't reset when notifyDataSetChange
- mOptionsContainer.setItemAnimator(null);
- }
-
- // Set aspect ratio on the preview card dynamically.
- Point mScreenSize;
- ScreenSizeCalculator screenSizeCalculator = ScreenSizeCalculator.getInstance();
- mScreenSize = screenSizeCalculator.getScreenSize(
- requireActivity().getWindowManager().getDefaultDisplay());
- ConstraintSet set = new ConstraintSet();
- set.clone(mContent);
- String ratio = String.format(Locale.US, "%d:%d", mScreenSize.x, mScreenSize.y);
- set.setDimensionRatio(R.id.preview_card_container, ratio);
- set.applyTo(mContent);
-
- mLoading = view.findViewById(R.id.loading_indicator);
- mError = view.findViewById(R.id.error_section);
-
- // For nav bar edge-to-edge effect.
- view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
- v.setPadding(
- v.getPaddingLeft(),
- windowInsets.getSystemWindowInsetTop(),
- v.getPaddingRight(),
- windowInsets.getSystemWindowInsetBottom());
- return windowInsets.consumeSystemWindowInsets();
- });
-
- // Clear memory cache whenever grid fragment view is being loaded.
- Glide.get(getContext()).clearMemory();
-
- mGridManager = GridOptionsManager.getInstance(getContext());
- mEventLogger = (ThemesUserEventLogger) InjectorProvider.getInjector()
- .getUserEventLogger(getContext());
- setUpOptions();
-
- SurfaceView wallpaperSurface = view.findViewById(R.id.wallpaper_preview_surface);
- WallpaperPreviewer wallpaperPreviewer = new WallpaperPreviewer(getLifecycle(),
- getActivity(), view.findViewById(R.id.wallpaper_preview_image), wallpaperSurface,
- view.findViewById(R.id.grid_fadein_scrim));
- // Loads current Wallpaper.
- CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector()
- .getCurrentWallpaperInfoFactory(getContext().getApplicationContext());
- factory.createCurrentWallpaperInfos((homeWallpaper, lockWallpaper, presentationMode) -> {
- mHomeWallpaper = homeWallpaper;
- wallpaperPreviewer.setWallpaper(mHomeWallpaper, /* listener= */ null);
- }, false);
-
- mGridOptionPreviewer = new GridOptionPreviewer(mGridManager,
- view.findViewById(R.id.grid_preview_container));
-
- return view;
- }
-
- @Override
- public boolean onBackPressed() {
- mGridOptionViewModel.setSelectedOption(null);
- mGridOptionViewModel.setBottomActionBarVisible(false);
- return super.onBackPressed();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (mGridOptionPreviewer != null) {
- mGridOptionPreviewer.release();
- }
- }
-
- @Override
- public CharSequence getDefaultTitle() {
- return getString(R.string.grid_title);
- }
-
- @Override
- protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
- super.onBottomActionBarReady(bottomActionBar);
- mBottomActionBar = bottomActionBar;
- mBottomActionBar.showActionsOnly(APPLY_TEXT);
- mBottomActionBar.setActionClickListener(APPLY_TEXT,
- v -> applyGridOption(mGridOptionViewModel.getSelectedOption()));
- mBottomActionBar.setActionAccessibilityTraversalAfter(APPLY_TEXT,
- mOptionsContainer.getId());
- }
-
- private void applyGridOption(GridOption gridOption) {
- mBottomActionBar.disableActions();
- mGridManager.apply(gridOption, mApplyGridCallback);
- }
-
- private void setUpOptions() {
- hideError();
- mLoading.show();
- mGridManager.fetchOptions(new OptionsFetchedListener<GridOption>() {
- @Override
- public void onOptionsLoaded(List<GridOption> options) {
- mLoading.hide();
- mOptionsController = new OptionSelectorController<>(
- mOptionsContainer, options, /* useGrid= */ false,
- CheckmarkStyle.CENTER_CHANGE_COLOR_WHEN_NOT_SELECTED);
- mOptionsController.initOptions(mGridManager);
- GridOption previouslySelectedOption = findEquivalent(options,
- mGridOptionViewModel.getSelectedOption());
- mGridOptionViewModel.setSelectedOption(
- previouslySelectedOption != null
- ? previouslySelectedOption
- : getActiveOption(options));
-
- mOptionsController.setSelectedOption(mGridOptionViewModel.getSelectedOption());
- onOptionSelected(mGridOptionViewModel.getSelectedOption());
- restoreBottomActionBarVisibility();
-
- mOptionsController.addListener(selectedOption -> {
- String title = selectedOption.getTitle();
- int stringId = R.string.option_previewed_description;
- if (selectedOption.isActive(mGridManager)) {
- stringId = R.string.option_applied_previewed_description;
- }
- CharSequence cd = getContext().getString(stringId, title);
- mOptionsContainer.announceForAccessibility(cd);
- onOptionSelected(selectedOption);
- mBottomActionBar.show();
- mGridOptionViewModel.setBottomActionBarVisible(true);
- });
- }
-
- @Override
- public void onError(@Nullable Throwable throwable) {
- if (throwable != null) {
- Log.e(TAG, "Error loading grid options", throwable);
- }
- showError();
- }
- }, /*reload= */ true);
- }
-
- private GridOption getActiveOption(List<GridOption> options) {
- return options.stream()
- .filter(option -> option.isActive(mGridManager))
- .findAny()
- // For development only, as there should always be a grid set.
- .orElse(options.get(0));
- }
-
- @Nullable
- private GridOption findEquivalent(List<GridOption> options, GridOption target) {
- return options.stream()
- .filter(option -> option.equals(target))
- .findAny()
- .orElse(null);
- }
-
- private void hideError() {
- mContent.setVisibility(View.VISIBLE);
- mError.setVisibility(View.GONE);
- }
-
- private void showError() {
- mLoading.hide();
- mContent.setVisibility(View.GONE);
- mError.setVisibility(View.VISIBLE);
- }
-
- private void onOptionSelected(CustomizationOption selectedOption) {
- mGridOptionViewModel.setSelectedOption((GridOption) selectedOption);
- mEventLogger.logGridSelected(mGridOptionViewModel.getSelectedOption());
- mGridOptionPreviewer.setGridOption(mGridOptionViewModel.getSelectedOption());
- }
-
- private void restoreBottomActionBarVisibility() {
- if (mGridOptionViewModel.getBottomActionBarVisible()) {
- mBottomActionBar.show();
- } else {
- mBottomActionBar.hide();
- }
- }
-}
diff --git a/src/com/android/customization/picker/grid/GridOptionPreviewer.java b/src/com/android/customization/picker/grid/GridOptionPreviewer.java
deleted file mode 100644
index 7786d35c..00000000
--- a/src/com/android/customization/picker/grid/GridOptionPreviewer.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2020 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.grid;
-
-import android.content.Context;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.ViewGroup;
-
-import com.android.customization.model.grid.GridOption;
-import com.android.customization.model.grid.GridOptionsManager;
-import com.android.wallpaper.R;
-import com.android.wallpaper.picker.WorkspaceSurfaceHolderCallback;
-import com.android.wallpaper.util.PreviewUtils;
-import com.android.wallpaper.util.SurfaceViewUtils;
-
-/** A class to load the {@link GridOption} preview to the view. */
-class GridOptionPreviewer {
-
- private final GridOptionsManager mGridManager;
- private final ViewGroup mPreviewContainer;
-
- private SurfaceView mGridOptionSurface;
- private GridOption mGridOption;
- private GridOptionSurfaceHolderCallback mSurfaceCallback;
-
- GridOptionPreviewer(GridOptionsManager gridManager, ViewGroup previewContainer) {
- mGridManager = gridManager;
- mPreviewContainer = previewContainer;
- }
-
- /** Loads the Grid option into the container view. */
- public void setGridOption(GridOption gridOption) {
- mGridOption = gridOption;
- if (mGridOption != null) {
- updateWorkspacePreview();
- }
- }
-
- /** Releases the view resource. */
- public void release() {
- if (mGridOptionSurface != null) {
- mSurfaceCallback.cleanUp();
- mGridOptionSurface = null;
- }
- mPreviewContainer.removeAllViews();
- }
-
- private void updateWorkspacePreview() {
- // Reattach SurfaceView to trigger #surfaceCreated to update preview for different option.
- mPreviewContainer.removeAllViews();
- if (mSurfaceCallback != null) {
- mSurfaceCallback.cleanUp();
- mSurfaceCallback.resetLastSurface();
- if (mGridOptionSurface != null) {
- mGridOptionSurface.getHolder().removeCallback(mSurfaceCallback);
- }
- }
- mGridOptionSurface = new SurfaceView(mPreviewContainer.getContext());
- mGridOptionSurface.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- mGridOptionSurface.setZOrderMediaOverlay(true);
- mSurfaceCallback = new GridOptionSurfaceHolderCallback(mGridOptionSurface,
- mGridOptionSurface.getContext());
- mGridOptionSurface.getHolder().addCallback(mSurfaceCallback);
- mPreviewContainer.addView(mGridOptionSurface);
- }
-
- private class GridOptionSurfaceHolderCallback extends WorkspaceSurfaceHolderCallback {
- private GridOptionSurfaceHolderCallback(SurfaceView workspaceSurface, Context context) {
- super(
- workspaceSurface,
- new PreviewUtils(
- context, context.getString(R.string.grid_control_metadata_name)));
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- if (mGridOption != null) {
- super.surfaceCreated(holder);
- }
- }
-
- @Override
- protected void requestPreview(SurfaceView workspaceSurface,
- PreviewUtils.WorkspacePreviewCallback callback) {
- mGridManager.renderPreview(
- SurfaceViewUtils.createSurfaceViewRequest(workspaceSurface),
- mGridOption.name, callback);
- }
- }
-}
diff --git a/src/com/android/customization/model/grid/data/repository/GridRepository.kt b/src/com/android/customization/picker/grid/data/repository/GridRepository.kt
index 4379dad5..f3844294 100644
--- a/src/com/android/customization/model/grid/data/repository/GridRepository.kt
+++ b/src/com/android/customization/picker/grid/data/repository/GridRepository.kt
@@ -15,15 +15,15 @@
*
*/
-package com.android.customization.model.grid.data.repository
+package com.android.customization.picker.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
-import com.android.customization.model.grid.shared.model.GridOptionItemsModel
+import com.android.customization.picker.grid.shared.model.GridOptionItemModel
+import com.android.customization.picker.grid.shared.model.GridOptionItemsModel
import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
diff --git a/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt b/src/com/android/customization/picker/grid/domain/interactor/GridInteractor.kt
index 7abd605b..02e16ddf 100644
--- a/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt
+++ b/src/com/android/customization/picker/grid/domain/interactor/GridInteractor.kt
@@ -15,13 +15,13 @@
*
*/
-package com.android.customization.model.grid.domain.interactor
+package com.android.customization.picker.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
+import com.android.customization.picker.grid.data.repository.GridRepository
+import com.android.customization.picker.grid.shared.model.GridOptionItemModel
+import com.android.customization.picker.grid.shared.model.GridOptionItemsModel
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
diff --git a/src/com/android/customization/model/grid/domain/interactor/GridSnapshotRestorer.kt b/src/com/android/customization/picker/grid/domain/interactor/GridSnapshotRestorer.kt
index 19d4c77e..74d77f75 100644
--- a/src/com/android/customization/model/grid/domain/interactor/GridSnapshotRestorer.kt
+++ b/src/com/android/customization/picker/grid/domain/interactor/GridSnapshotRestorer.kt
@@ -15,10 +15,10 @@
*
*/
-package com.android.customization.model.grid.domain.interactor
+package com.android.customization.picker.grid.domain.interactor
import android.util.Log
-import com.android.customization.model.grid.shared.model.GridOptionItemModel
+import com.android.customization.picker.grid.shared.model.GridOptionItemModel
import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
import com.android.wallpaper.picker.undo.domain.interactor.SnapshotStore
import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot
diff --git a/src/com/android/customization/model/grid/shared/model/GridOptionItemModel.kt b/src/com/android/customization/picker/grid/shared/model/GridOptionItemModel.kt
index 2eabeab5..1fb01be0 100644
--- a/src/com/android/customization/model/grid/shared/model/GridOptionItemModel.kt
+++ b/src/com/android/customization/picker/grid/shared/model/GridOptionItemModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.model.grid.shared.model
+package com.android.customization.picker.grid.shared.model
import kotlinx.coroutines.flow.StateFlow
diff --git a/src/com/android/customization/model/grid/shared/model/GridOptionItemsModel.kt b/src/com/android/customization/picker/grid/shared/model/GridOptionItemsModel.kt
index e969be88..e5b33c52 100644
--- a/src/com/android/customization/model/grid/shared/model/GridOptionItemsModel.kt
+++ b/src/com/android/customization/picker/grid/shared/model/GridOptionItemsModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.model.grid.shared.model
+package com.android.customization.picker.grid.shared.model
sealed class GridOptionItemsModel {
data class Loaded(
diff --git a/src/com/android/customization/model/grid/ui/binder/GridIconViewBinder.kt b/src/com/android/customization/picker/grid/ui/binder/GridIconViewBinder.kt
index fba89a74..9fc88a0e 100644
--- a/src/com/android/customization/model/grid/ui/binder/GridIconViewBinder.kt
+++ b/src/com/android/customization/picker/grid/ui/binder/GridIconViewBinder.kt
@@ -1,7 +1,7 @@
-package com.android.customization.model.grid.ui.binder
+package com.android.customization.picker.grid.ui.binder
import android.widget.ImageView
-import com.android.customization.model.grid.ui.viewmodel.GridIconViewModel
+import com.android.customization.picker.grid.ui.viewmodel.GridIconViewModel
import com.android.customization.widget.GridTileDrawable
object GridIconViewBinder {
diff --git a/src/com/android/customization/model/grid/ui/binder/GridScreenBinder.kt b/src/com/android/customization/picker/grid/ui/binder/GridScreenBinder.kt
index 56fe425d..bcb37379 100644
--- a/src/com/android/customization/model/grid/ui/binder/GridScreenBinder.kt
+++ b/src/com/android/customization/picker/grid/ui/binder/GridScreenBinder.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.model.grid.ui.binder
+package com.android.customization.picker.grid.ui.binder
import android.view.View
import android.widget.Button
@@ -26,9 +26,9 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.customization.model.grid.ui.viewmodel.GridIconViewModel
-import com.android.customization.model.grid.ui.viewmodel.GridScreenViewModel
import com.android.customization.picker.common.ui.view.ItemSpacing
+import com.android.customization.picker.grid.ui.viewmodel.GridIconViewModel
+import com.android.customization.picker.grid.ui.viewmodel.GridScreenViewModel
import com.android.wallpaper.R
import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
import com.android.wallpaper.picker.option.ui.binder.OptionItemBinder
@@ -55,7 +55,7 @@ object GridScreenBinder {
optionView.addItemDecoration(ItemSpacing(ItemSpacing.ITEM_SPACING_DP))
val adapter =
OptionItemAdapter(
- layoutResourceId = R.layout.grid_option_2,
+ layoutResourceId = R.layout.grid_option,
lifecycleOwner = lifecycleOwner,
backgroundDispatcher = backgroundDispatcher,
foregroundTintSpec =
diff --git a/src/com/android/customization/model/grid/ui/fragment/GridFragment2.kt b/src/com/android/customization/picker/grid/ui/fragment/GridFragment.kt
index 9e99efee..2a301b40 100644
--- a/src/com/android/customization/model/grid/ui/fragment/GridFragment2.kt
+++ b/src/com/android/customization/picker/grid/ui/fragment/GridFragment.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.model.grid.ui.fragment
+package com.android.customization.picker.grid.ui.fragment
import android.os.Bundle
import android.util.Log
@@ -30,10 +30,10 @@ 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.customization.picker.grid.domain.interactor.GridInteractor
+import com.android.customization.picker.grid.ui.binder.GridScreenBinder
+import com.android.customization.picker.grid.ui.viewmodel.GridScreenViewModel
import com.android.wallpaper.R
import com.android.wallpaper.config.BaseFlags
import com.android.wallpaper.module.CurrentWallpaperInfoFactory
@@ -48,10 +48,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
-private val TAG = GridFragment2::class.java.simpleName
+private val TAG = GridFragment::class.java.simpleName
@OptIn(ExperimentalCoroutinesApi::class)
-class GridFragment2 : AppbarFragment() {
+class GridFragment : AppbarFragment() {
private lateinit var gridInteractor: GridInteractor
@@ -185,11 +185,11 @@ class GridFragment2 : AppbarFragment() {
wallpaperInfoProvider = {
suspendCancellableCoroutine { continuation ->
wallpaperInfoFactory.createCurrentWallpaperInfos(
- { homeWallpaper, lockWallpaper, _ ->
- continuation.resume(homeWallpaper ?: lockWallpaper, null)
- },
+ context,
/* forceRefresh= */ true,
- )
+ ) { homeWallpaper, lockWallpaper, _ ->
+ continuation.resume(homeWallpaper ?: lockWallpaper, null)
+ }
}
},
wallpaperInteractor = wallpaperInteractor,
diff --git a/src/com/android/customization/model/grid/GridSectionController.java b/src/com/android/customization/picker/grid/ui/section/GridSectionController.java
index c50bfcc2..6ae9acd9 100644
--- a/src/com/android/customization/model/grid/GridSectionController.java
+++ b/src/com/android/customization/picker/grid/ui/section/GridSectionController.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.customization.model.grid;
+package com.android.customization.picker.grid.ui.section;
import android.content.Context;
import android.util.Log;
@@ -27,9 +27,10 @@ import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
-import com.android.customization.model.grid.ui.fragment.GridFragment2;
-import com.android.customization.picker.grid.GridFragment;
-import com.android.customization.picker.grid.GridSectionView;
+import com.android.customization.model.grid.GridOption;
+import com.android.customization.model.grid.GridOptionsManager;
+import com.android.customization.picker.grid.ui.fragment.GridFragment;
+import com.android.customization.picker.grid.ui.view.GridSectionView;
import com.android.wallpaper.R;
import com.android.wallpaper.model.CustomizationSectionController;
@@ -42,7 +43,6 @@ public class GridSectionController implements CustomizationSectionController<Gri
private final GridOptionsManager mGridOptionsManager;
private final CustomizationSectionNavigationController mSectionNavigationController;
- private final boolean mIsRevampedUiEnabled;
private final Observer<Object> mOptionChangeObserver;
private final LifecycleOwner mLifecycleOwner;
private TextView mSectionDescription;
@@ -55,7 +55,6 @@ public class GridSectionController implements CustomizationSectionController<Gri
boolean isRevampedUiEnabled) {
mGridOptionsManager = gridOptionsManager;
mSectionNavigationController = sectionNavigationController;
- mIsRevampedUiEnabled = isRevampedUiEnabled;
mLifecycleOwner = lifecycleOwner;
mOptionChangeObserver = o -> updateUi(/* reload= */ true);
}
@@ -74,20 +73,13 @@ public class GridSectionController implements CustomizationSectionController<Gri
// Fetch grid options to show currently set grid.
updateUi(/* The result is getting when calling isAvailable(), so reload= */ false);
- if (mIsRevampedUiEnabled) {
- mGridOptionsManager.getOptionChangeObservable(/* handler= */ null).observe(
- mLifecycleOwner,
- mOptionChangeObserver);
- }
+ mGridOptionsManager.getOptionChangeObservable(/* handler= */ null).observe(
+ mLifecycleOwner,
+ mOptionChangeObserver);
gridSectionView.setOnClickListener(
v -> {
- final Fragment gridFragment;
- if (mIsRevampedUiEnabled) {
- gridFragment = new GridFragment2();
- } else {
- gridFragment = new GridFragment();
- }
+ final Fragment gridFragment = new GridFragment();
mSectionNavigationController.navigateTo(gridFragment);
});
@@ -96,7 +88,7 @@ public class GridSectionController implements CustomizationSectionController<Gri
@Override
public void release() {
- if (mIsRevampedUiEnabled && mGridOptionsManager.isAvailable()) {
+ if (mGridOptionsManager.isAvailable()) {
mGridOptionsManager.getOptionChangeObservable(/* handler= */ null).removeObserver(
mOptionChangeObserver
);
diff --git a/src/com/android/customization/picker/grid/GridSectionView.java b/src/com/android/customization/picker/grid/ui/view/GridSectionView.java
index 58468e10..545ef197 100644
--- a/src/com/android/customization/picker/grid/GridSectionView.java
+++ b/src/com/android/customization/picker/grid/ui/view/GridSectionView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.customization.picker.grid;
+package com.android.customization.picker.grid.ui.view;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/src/com/android/customization/model/grid/ui/viewmodel/GridIconViewModel.kt b/src/com/android/customization/picker/grid/ui/viewmodel/GridIconViewModel.kt
index 3942d7cc..d12dc6c3 100644
--- a/src/com/android/customization/model/grid/ui/viewmodel/GridIconViewModel.kt
+++ b/src/com/android/customization/picker/grid/ui/viewmodel/GridIconViewModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.model.grid.ui.viewmodel
+package com.android.customization.picker.grid.ui.viewmodel
data class GridIconViewModel(
val columns: Int,
diff --git a/src/com/android/customization/model/grid/ui/viewmodel/GridScreenViewModel.kt b/src/com/android/customization/picker/grid/ui/viewmodel/GridScreenViewModel.kt
index c11a5947..179127d1 100644
--- a/src/com/android/customization/model/grid/ui/viewmodel/GridScreenViewModel.kt
+++ b/src/com/android/customization/picker/grid/ui/viewmodel/GridScreenViewModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.model.grid.ui.viewmodel
+package com.android.customization.picker.grid.ui.viewmodel
import android.annotation.SuppressLint
import android.content.Context
@@ -24,8 +24,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.customization.model.ResourceConstants
-import com.android.customization.model.grid.domain.interactor.GridInteractor
-import com.android.customization.model.grid.shared.model.GridOptionItemsModel
+import com.android.customization.picker.grid.domain.interactor.GridInteractor
+import com.android.customization.picker.grid.shared.model.GridOptionItemsModel
import com.android.wallpaper.picker.common.text.ui.viewmodel.Text
import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel
import kotlinx.coroutines.flow.Flow
diff --git a/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModel.kt b/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModel.kt
index 954efa24..1a5254f8 100644
--- a/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModel.kt
+++ b/src/com/android/customization/picker/notifications/ui/viewmodel/NotificationSectionViewModel.kt
@@ -21,6 +21,7 @@ import androidx.annotation.VisibleForTesting
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
+import com.android.customization.module.logging.ThemesUserEventLogger
import com.android.customization.picker.notifications.domain.interactor.NotificationsInteractor
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@@ -31,6 +32,7 @@ class NotificationSectionViewModel
@VisibleForTesting
constructor(
private val interactor: NotificationsInteractor,
+ private val logger: ThemesUserEventLogger,
) : ViewModel() {
/** Whether the switch should be on. */
@@ -39,16 +41,23 @@ constructor(
/** Notifies that the section has been clicked. */
fun onClicked() {
- viewModelScope.launch { interactor.toggleShowNotificationsOnLockScreenEnabled() }
+ viewModelScope.launch {
+ interactor.toggleShowNotificationsOnLockScreenEnabled()
+ logger.logLockScreenNotificationApplied(
+ interactor.getSettings().isShowNotificationsOnLockScreenEnabled
+ )
+ }
}
class Factory(
private val interactor: NotificationsInteractor,
+ private val logger: ThemesUserEventLogger,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return NotificationSectionViewModel(
interactor = interactor,
+ logger = logger,
)
as T
}
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 71dfe1da..eb25af7a 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
@@ -42,10 +42,10 @@ import com.android.customization.picker.color.domain.interactor.ColorPickerInter
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
import com.android.wallpaper.module.CurrentWallpaperInfoFactory
import com.android.wallpaper.module.CustomizationSections
+import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewClickView
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewSectionController
@@ -64,7 +64,7 @@ class PreviewWithClockCarouselSectionController(
private val lifecycleOwner: LifecycleOwner,
private val screen: CustomizationSections.Screen,
wallpaperInfoFactory: CurrentWallpaperInfoFactory,
- colorViewModel: WallpaperColorsViewModel,
+ wallpaperColorsRepository: WallpaperColorsRepository,
displayUtils: DisplayUtils,
clockCarouselViewModelFactory: ClockCarouselViewModel.Factory,
private val clockViewFactory: ClockViewFactory,
@@ -82,7 +82,7 @@ class PreviewWithClockCarouselSectionController(
lifecycleOwner,
screen,
wallpaperInfoFactory,
- colorViewModel,
+ wallpaperColorsRepository,
displayUtils,
wallpaperPreviewNavigator,
wallpaperInteractor,
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 c4d6be45..b3e778ba 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithThemeSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithThemeSectionController.kt
@@ -25,16 +25,17 @@ import com.android.customization.model.themedicon.domain.interactor.ThemedIconIn
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
import com.android.wallpaper.model.WallpaperPreviewNavigator
import com.android.wallpaper.module.CurrentWallpaperInfoFactory
import com.android.wallpaper.module.CustomizationSections
+import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository
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
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
/**
@@ -46,7 +47,7 @@ open class PreviewWithThemeSectionController(
lifecycleOwner: LifecycleOwner,
private val screen: CustomizationSections.Screen,
private val wallpaperInfoFactory: CurrentWallpaperInfoFactory,
- private val colorViewModel: WallpaperColorsViewModel,
+ private val wallpaperColorsRepository: WallpaperColorsRepository,
displayUtils: DisplayUtils,
wallpaperPreviewNavigator: WallpaperPreviewNavigator,
private val wallpaperInteractor: WallpaperInteractor,
@@ -61,7 +62,7 @@ open class PreviewWithThemeSectionController(
lifecycleOwner,
screen,
wallpaperInfoFactory,
- colorViewModel,
+ wallpaperColorsRepository,
displayUtils,
wallpaperPreviewNavigator,
wallpaperInteractor,
@@ -69,6 +70,7 @@ open class PreviewWithThemeSectionController(
isTwoPaneAndSmallWidth,
customizationPickerViewModel,
) {
+ @OptIn(ExperimentalCoroutinesApi::class)
override fun createScreenPreviewViewModel(context: Context): ScreenPreviewViewModel {
return PreviewWithThemeViewModel(
previewUtils =
@@ -92,28 +94,28 @@ open class PreviewWithThemeSectionController(
wallpaperInfoProvider = { forceReload ->
suspendCancellableCoroutine { continuation ->
wallpaperInfoFactory.createCurrentWallpaperInfos(
- { homeWallpaper, lockWallpaper, _ ->
- val wallpaper =
- if (isOnLockScreen) {
- lockWallpaper ?: homeWallpaper
- } else {
- homeWallpaper ?: lockWallpaper
- }
- loadInitialColors(
- context = context,
- screen = screen,
- )
- continuation.resume(wallpaper, null)
- },
+ context,
forceReload,
- )
+ ) { homeWallpaper, lockWallpaper, _ ->
+ val wallpaper =
+ if (isOnLockScreen) {
+ lockWallpaper ?: homeWallpaper
+ } else {
+ homeWallpaper ?: lockWallpaper
+ }
+ loadInitialColors(
+ context = context,
+ screen = screen,
+ )
+ continuation.resume(wallpaper, null)
+ }
}
},
onWallpaperColorChanged = { colors ->
if (isOnLockScreen) {
- colorViewModel.setLockWallpaperColors(colors)
+ wallpaperColorsRepository.setLockWallpaperColors(colors)
} else {
- colorViewModel.setHomeWallpaperColors(colors)
+ wallpaperColorsRepository.setHomeWallpaperColors(colors)
}
},
initialExtrasProvider = { getInitialExtras(isOnLockScreen) },
diff --git a/src/com/android/customization/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt b/src/com/android/customization/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt
index b17af80d..6bfe3484 100644
--- a/src/com/android/customization/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt
+++ b/src/com/android/customization/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt
@@ -21,11 +21,11 @@ import com.android.customization.picker.quickaffordance.shared.model.KeyguardQui
import com.android.customization.picker.quickaffordance.shared.model.KeyguardQuickAffordancePickerSelectionModel as SelectionModel
import com.android.customization.picker.quickaffordance.shared.model.KeyguardQuickAffordancePickerSlotModel as SlotModel
import com.android.systemui.shared.customization.data.content.CustomizationProviderClient as Client
-import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
-import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.flow.shareIn
/**
* Abstracts access to application state related to functionality for selecting, picking, or setting
@@ -33,39 +33,25 @@ import kotlinx.coroutines.withContext
*/
class KeyguardQuickAffordancePickerRepository(
private val client: Client,
- private val backgroundDispatcher: CoroutineDispatcher,
+ private val scope: CoroutineScope
) {
- /** Whether the feature is enabled. */
- val isFeatureEnabled: Flow<Boolean> =
- client.observeFlags().map { flags -> flags.isFeatureEnabled() }
-
/** List of slots available on the device. */
val slots: Flow<List<SlotModel>> =
client.observeSlots().map { slots -> slots.map { slot -> slot.toModel() } }
/** List of all available quick affordances. */
val affordances: Flow<List<AffordanceModel>> =
- client.observeAffordances().map { affordances ->
- affordances.map { affordance -> affordance.toModel() }
- }
+ client
+ .observeAffordances()
+ .map { affordances -> affordances.map { affordance -> affordance.toModel() } }
+ .shareIn(scope, replay = 1, started = SharingStarted.Lazily)
/** List of slot-affordance pairs, modeling what the user has currently chosen for each slot. */
val selections: Flow<List<SelectionModel>> =
- client.observeSelections().map { selections ->
- selections.map { selection -> selection.toModel() }
- }
-
- suspend fun isFeatureEnabled(): Boolean {
- return withContext(backgroundDispatcher) { client.queryFlags().isFeatureEnabled() }
- }
-
- private fun List<Client.Flag>.isFeatureEnabled(): Boolean {
- return find { flag ->
- flag.name ==
- Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED
- }
- ?.value == true
- }
+ client
+ .observeSelections()
+ .map { selections -> selections.map { selection -> selection.toModel() } }
+ .shareIn(scope, replay = 1, started = SharingStarted.Lazily)
private fun Client.Slot.toModel(): SlotModel {
return SlotModel(
diff --git a/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt b/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt
index f154de65..3eca6241 100644
--- a/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt
+++ b/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt
@@ -64,7 +64,7 @@ class KeyguardQuickAffordancePickerInteractor(
}
/** Unselects all affordances from the slot with the given ID. */
- suspend fun unselectAll(slotId: String) {
+ suspend fun unselectAllFromSlot(slotId: String) {
client.deleteAllSelections(
slotId = slotId,
)
@@ -72,15 +72,15 @@ class KeyguardQuickAffordancePickerInteractor(
snapshotRestorer.get().storeSnapshot()
}
+ /** Unselects all affordances from all slots. */
+ suspend fun unselectAll() {
+ client.querySlots().forEach { client.deleteAllSelections(it.id) }
+ }
+
/** Returns a [Drawable] for the given resource ID, from the system UI package. */
suspend fun getAffordanceIcon(
@DrawableRes iconResourceId: Int,
): Drawable {
return client.getAffordanceIcon(iconResourceId)
}
-
- /** Returns `true` if the feature is enabled; `false` otherwise. */
- suspend fun isFeatureEnabled(): Boolean {
- return repository.isFeatureEnabled()
- }
}
diff --git a/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordanceSnapshotRestorer.kt b/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordanceSnapshotRestorer.kt
index 3c7928ce..fee0cb51 100644
--- a/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordanceSnapshotRestorer.kt
+++ b/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordanceSnapshotRestorer.kt
@@ -42,9 +42,14 @@ class KeyguardQuickAffordanceSnapshotRestorer(
}
override suspend fun restoreToSnapshot(snapshot: RestorableSnapshot) {
+ // reset all current selections
+ interactor.unselectAll()
+
+ val allSelections = checkNotNull(snapshot.args[KEY_SELECTIONS])
+ if (allSelections.isEmpty()) return
+
val selections: List<Pair<String, String>> =
- checkNotNull(snapshot.args[KEY_SELECTIONS]).split(SELECTION_SEPARATOR).map { selection
- ->
+ allSelections.split(SELECTION_SEPARATOR).map { selection ->
val (slotId, affordanceId) = selection.split(SLOT_AFFORDANCE_SEPARATOR)
slotId to affordanceId
}
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 8891b03f..0e3b7167 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
@@ -67,7 +67,9 @@ class SlotTabAdapter : RecyclerView.Adapter<SlotTabAdapter.ViewHolder>() {
.find { it.isSelected.value }
?.text
?.asString(holder.itemView.context)
- stateDescription?.let { holder.itemView.stateDescription = it }
+ holder.itemView.stateDescription =
+ stateDescription
+ ?: holder.itemView.resources.getString(R.string.keyguard_affordance_none)
}
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 091f484e..3ac52ad5 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
@@ -20,7 +20,11 @@ package com.android.customization.picker.quickaffordance.ui.binder
import android.app.Dialog
import android.content.Context
import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
import android.widget.ImageView
+import androidx.core.view.AccessibilityDelegateCompat
+import androidx.core.view.ViewCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
@@ -62,6 +66,26 @@ object KeyguardQuickAffordancePickerBinder {
slotTabView.layoutManager =
LinearLayoutManager(view.context, RecyclerView.HORIZONTAL, false)
slotTabView.addItemDecoration(ItemSpacing(ItemSpacing.TAB_ITEM_SPACING_DP))
+
+ // Setting a custom accessibility delegate so that the default content descriptions
+ // for items in a list aren't announced (for left & right shortcuts). We populate
+ // the content description for these shortcuts later on with the right (expected)
+ // values.
+ val slotTabViewDelegate: AccessibilityDelegateCompat =
+ object : AccessibilityDelegateCompat() {
+ override fun onRequestSendAccessibilityEvent(
+ host: ViewGroup,
+ child: View,
+ event: AccessibilityEvent
+ ): Boolean {
+ if (event.eventType != AccessibilityEvent.TYPE_VIEW_FOCUSED) {
+ child.contentDescription = null
+ }
+ return super.onRequestSendAccessibilityEvent(host, child, event)
+ }
+ }
+
+ ViewCompat.setAccessibilityDelegate(slotTabView, slotTabViewDelegate)
val affordancesAdapter =
OptionItemAdapter(
layoutResourceId = R.layout.keyguard_quick_affordance,
diff --git a/src/com/android/customization/picker/quickaffordance/ui/section/KeyguardQuickAffordanceSectionController.kt b/src/com/android/customization/picker/quickaffordance/ui/section/KeyguardQuickAffordanceSectionController.kt
index e0beeff0..0c7b250d 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/section/KeyguardQuickAffordanceSectionController.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/section/KeyguardQuickAffordanceSectionController.kt
@@ -20,27 +20,23 @@ package com.android.customization.picker.quickaffordance.ui.section
import android.content.Context
import android.view.LayoutInflater
import androidx.lifecycle.LifecycleOwner
-import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
import com.android.customization.picker.quickaffordance.ui.binder.KeyguardQuickAffordanceSectionViewBinder
import com.android.customization.picker.quickaffordance.ui.fragment.KeyguardQuickAffordancePickerFragment
import com.android.customization.picker.quickaffordance.ui.view.KeyguardQuickAffordanceSectionView
import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
import com.android.wallpaper.R
+import com.android.wallpaper.config.BaseFlags
import com.android.wallpaper.model.CustomizationSectionController
import com.android.wallpaper.model.CustomizationSectionController.CustomizationSectionNavigationController as NavigationController
-import kotlinx.coroutines.runBlocking
class KeyguardQuickAffordanceSectionController(
private val navigationController: NavigationController,
- private val interactor: KeyguardQuickAffordancePickerInteractor,
private val viewModel: KeyguardQuickAffordancePickerViewModel,
private val lifecycleOwner: LifecycleOwner,
) : CustomizationSectionController<KeyguardQuickAffordanceSectionView> {
- private val isFeatureEnabled: Boolean = runBlocking { interactor.isFeatureEnabled() }
-
override fun isAvailable(context: Context): Boolean {
- return isFeatureEnabled
+ return BaseFlags.get().isKeyguardQuickAffordanceEnabled(context)
}
override fun createView(context: Context): KeyguardQuickAffordanceSectionView {
diff --git a/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
index f832cdeb..260c0d3b 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
@@ -26,6 +26,7 @@ import androidx.annotation.DrawableRes
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
+import com.android.customization.module.logging.ThemesUserEventLogger
import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
@@ -63,6 +64,7 @@ private constructor(
private val quickAffordanceInteractor: KeyguardQuickAffordancePickerInteractor,
private val wallpaperInteractor: WallpaperInteractor,
private val wallpaperInfoFactory: CurrentWallpaperInfoFactory,
+ private val logger: ThemesUserEventLogger,
) : ViewModel() {
@SuppressLint("StaticFieldLeak") private val applicationContext = context.applicationContext
@@ -92,11 +94,11 @@ private constructor(
wallpaperInfoProvider = { forceReload ->
suspendCancellableCoroutine { continuation ->
wallpaperInfoFactory.createCurrentWallpaperInfos(
- { homeWallpaper, lockWallpaper, _ ->
- continuation.resume(lockWallpaper ?: homeWallpaper, null)
- },
+ context,
forceReload,
- )
+ ) { homeWallpaper, lockWallpaper, _ ->
+ continuation.resume(lockWallpaper ?: homeWallpaper, null)
+ }
}
},
wallpaperInteractor = wallpaperInteractor,
@@ -158,7 +160,8 @@ private constructor(
Icon.Loaded(
drawable =
getAffordanceIcon(affordanceModel.iconResourceId),
- contentDescription = null,
+ contentDescription =
+ Text.Loaded(getSlotContentDescription(slot.id)),
),
text = Text.Loaded(affordanceModel.name),
isSelected = MutableStateFlow(true) as StateFlow<Boolean>,
@@ -214,7 +217,13 @@ private constructor(
if (!isSelected) {
{
viewModelScope.launch {
- quickAffordanceInteractor.unselectAll(selectedSlotId)
+ quickAffordanceInteractor.unselectAllFromSlot(
+ selectedSlotId
+ )
+ logger.logShortcutApplied(
+ shortcut = "none",
+ shortcutSlotId = selectedSlotId,
+ )
}
}
} else {
@@ -250,6 +259,10 @@ private constructor(
slotId = selectedSlotId,
affordanceId = affordance.id,
)
+ logger.logShortcutApplied(
+ shortcut = affordance.id,
+ shortcutSlotId = selectedSlotId,
+ )
}
}
} else {
@@ -423,6 +436,18 @@ private constructor(
)
}
+ private fun getSlotContentDescription(slotId: String): String {
+ return applicationContext.getString(
+ when (slotId) {
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START ->
+ R.string.keyguard_slot_name_bottom_start
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END ->
+ R.string.keyguard_slot_name_bottom_end
+ else -> error("No accessibility label for slot with ID \"$slotId\"!")
+ }
+ )
+ }
+
private suspend fun getAffordanceIcon(@DrawableRes iconResourceId: Int): Drawable {
return quickAffordanceInteractor.getAffordanceIcon(iconResourceId)
}
@@ -463,6 +488,7 @@ private constructor(
private val quickAffordanceInteractor: KeyguardQuickAffordancePickerInteractor,
private val wallpaperInteractor: WallpaperInteractor,
private val wallpaperInfoFactory: CurrentWallpaperInfoFactory,
+ private val logger: ThemesUserEventLogger,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
@@ -471,6 +497,7 @@ private constructor(
quickAffordanceInteractor = quickAffordanceInteractor,
wallpaperInteractor = wallpaperInteractor,
wallpaperInfoFactory = wallpaperInfoFactory,
+ logger = logger,
)
as T
}
diff --git a/src/com/android/customization/picker/theme/CustomThemeActivity.java b/src/com/android/customization/picker/theme/CustomThemeActivity.java
deleted file mode 100644
index 62a2f266..00000000
--- a/src/com/android/customization/picker/theme/CustomThemeActivity.java
+++ /dev/null
@@ -1,421 +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.theme;
-
-import android.app.AlertDialog.Builder;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentTransaction;
-
-import com.android.customization.model.CustomizationManager.Callback;
-import com.android.customization.model.theme.DefaultThemeProvider;
-import com.android.customization.model.theme.OverlayManagerCompat;
-import com.android.customization.model.theme.ThemeBundle;
-import com.android.customization.model.theme.ThemeBundleProvider;
-import com.android.customization.model.theme.ThemeManager;
-import com.android.customization.model.theme.custom.ColorOptionsProvider;
-import com.android.customization.model.theme.custom.CustomTheme;
-import com.android.customization.model.theme.custom.CustomThemeManager;
-import com.android.customization.model.theme.custom.FontOptionsProvider;
-import com.android.customization.model.theme.custom.IconOptionsProvider;
-import com.android.customization.model.theme.custom.ShapeOptionsProvider;
-import com.android.customization.model.theme.custom.ThemeComponentOption;
-import com.android.customization.model.theme.custom.ThemeComponentOption.ColorOption;
-import com.android.customization.model.theme.custom.ThemeComponentOption.FontOption;
-import com.android.customization.model.theme.custom.ThemeComponentOption.IconOption;
-import com.android.customization.model.theme.custom.ThemeComponentOption.ShapeOption;
-import com.android.customization.model.theme.custom.ThemeComponentOptionProvider;
-import com.android.customization.module.CustomizationInjector;
-import com.android.customization.module.ThemesUserEventLogger;
-import com.android.customization.picker.theme.CustomThemeStepFragment.CustomThemeComponentStepHost;
-import com.android.wallpaper.R;
-import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.picker.AppbarFragment.AppbarFragmentHost;
-
-import org.json.JSONException;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class CustomThemeActivity extends FragmentActivity implements
- AppbarFragmentHost, CustomThemeComponentStepHost {
- public static final String EXTRA_THEME_ID = "CustomThemeActivity.ThemeId";
- public static final String EXTRA_THEME_TITLE = "CustomThemeActivity.ThemeTitle";
- public static final String EXTRA_THEME_PACKAGES = "CustomThemeActivity.ThemePackages";
- public static final int REQUEST_CODE_CUSTOM_THEME = 1;
- public static final int RESULT_THEME_DELETED = 10;
- public static final int RESULT_THEME_APPLIED = 20;
-
- private static final String TAG = "CustomThemeActivity";
- private static final String KEY_STATE_CURRENT_STEP = "CustomThemeActivity.currentStep";
-
- private ThemesUserEventLogger mUserEventLogger;
- private List<ComponentStep<?>> mSteps;
- private int mCurrentStep;
- private CustomThemeManager mCustomThemeManager;
- private ThemeManager mThemeManager;
- private TextView mNextButton;
- private TextView mPreviousButton;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
- mUserEventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(this);
- ThemeBundleProvider themeProvider =
- new DefaultThemeProvider(this, injector.getCustomizationPreferences(this));
- Intent intent = getIntent();
- CustomTheme customTheme = null;
- if (intent != null && intent.hasExtra(EXTRA_THEME_PACKAGES)
- && intent.hasExtra(EXTRA_THEME_TITLE) && intent.hasExtra(EXTRA_THEME_ID)) {
- try {
- CustomTheme.Builder themeBuilder = themeProvider.parseCustomTheme(
- intent.getStringExtra(EXTRA_THEME_PACKAGES));
- if (themeBuilder != null) {
- themeBuilder.setId(intent.getStringExtra(EXTRA_THEME_ID));
- themeBuilder.setTitle(intent.getStringExtra(EXTRA_THEME_TITLE));
- customTheme = themeBuilder.build(this);
- }
- } catch (JSONException e) {
- Log.w(TAG, "Couldn't parse provided custom theme, will override it");
- }
- }
-
- mThemeManager = injector.getThemeManager(
- new DefaultThemeProvider(this, injector.getCustomizationPreferences(this)),
- this,
- new OverlayManagerCompat(this),
- mUserEventLogger);
- mThemeManager.fetchOptions(null, false);
- mCustomThemeManager = CustomThemeManager.create(customTheme, mThemeManager);
- if (savedInstanceState != null) {
- mCustomThemeManager.readCustomTheme(themeProvider, savedInstanceState);
- }
-
- int currentStep = 0;
- if (savedInstanceState != null) {
- currentStep = savedInstanceState.getInt(KEY_STATE_CURRENT_STEP);
- }
- initSteps(currentStep);
-
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_custom_theme);
- mNextButton = findViewById(R.id.next_button);
- mNextButton.setOnClickListener(view -> onNextOrApply());
- mPreviousButton = findViewById(R.id.previous_button);
- mPreviousButton.setOnClickListener(view -> onBackPressed());
-
- FragmentManager fm = getSupportFragmentManager();
- Fragment fragment = fm.findFragmentById(R.id.fragment_container);
- if (fragment == null) {
- // Navigate to the first step
- navigateToStep(0);
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(KEY_STATE_CURRENT_STEP, mCurrentStep);
- if (mCustomThemeManager != null) {
- mCustomThemeManager.saveCustomTheme(this, outState);
- }
- }
-
- private void navigateToStep(int i) {
- FragmentManager fragmentManager = getSupportFragmentManager();
- ComponentStep step = mSteps.get(i);
- Fragment fragment = step.getFragment(mCustomThemeManager.getOriginalTheme().getTitle());
-
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
- fragmentTransaction.replace(R.id.fragment_container, fragment);
- // Don't add step 0 to the back stack so that going back from it just finishes the Activity
- if (i > 0) {
- fragmentTransaction.addToBackStack("Step " + i);
- }
- fragmentTransaction.commit();
- fragmentManager.executePendingTransactions();
- updateNavigationButtonLabels();
- }
-
- private void initSteps(int currentStep) {
- mSteps = new ArrayList<>();
- OverlayManagerCompat manager = new OverlayManagerCompat(this);
- mSteps.add(new FontStep(new FontOptionsProvider(this, manager), 0));
- mSteps.add(new IconStep(new IconOptionsProvider(this, manager), 1));
- mSteps.add(new ColorStep(new ColorOptionsProvider(this, manager, mCustomThemeManager), 2));
- mSteps.add(new ShapeStep(new ShapeOptionsProvider(this, manager), 3));
- mSteps.add(new NameStep(4));
- mCurrentStep = currentStep;
- }
-
- private void onNextOrApply() {
- CustomThemeStepFragment stepFragment = getCurrentStepFragment();
- if (stepFragment instanceof CustomThemeComponentFragment) {
- CustomThemeComponentFragment fragment = (CustomThemeComponentFragment) stepFragment;
- mCustomThemeManager.apply(fragment.getSelectedOption(), new Callback() {
- @Override
- public void onSuccess() {
- navigateToStep(mCurrentStep + 1);
- }
-
- @Override
- public void onError(@Nullable Throwable throwable) {
- Log.w(TAG, "Error applying custom theme component", throwable);
- Toast.makeText(CustomThemeActivity.this, R.string.apply_theme_error_msg,
- Toast.LENGTH_LONG).show();
- }
- });
- } else if (stepFragment instanceof CustomThemeNameFragment) {
- CustomThemeNameFragment fragment = (CustomThemeNameFragment) stepFragment;
- CustomTheme originalTheme = mCustomThemeManager.getOriginalTheme();
-
- // We're on the last step, apply theme and leave
- CustomTheme themeToApply = mCustomThemeManager.buildPartialCustomTheme(this,
- originalTheme.getId(), fragment.getThemeName());
-
- // If the current theme is equal to the original theme being edited, then
- // don't search for an equivalent, let the user apply the same one by keeping
- // it null.
- ThemeBundle equivalent = (originalTheme.isEquivalent(themeToApply))
- ? null : mThemeManager.findThemeByPackages(themeToApply);
-
- if (equivalent != null) {
- Builder builder =
- new Builder(CustomThemeActivity.this);
- builder.setTitle(getString(R.string.use_style_instead_title,
- equivalent.getTitle()))
- .setMessage(getString(R.string.use_style_instead_body,
- equivalent.getTitle()))
- .setPositiveButton(getString(R.string.use_style_button,
- equivalent.getTitle()),
- (dialogInterface, i) -> applyTheme(equivalent))
- .setNegativeButton(R.string.no_thanks, null)
- .create()
- .show();
- } else {
- applyTheme(themeToApply);
- }
- } else {
- throw new IllegalStateException("Unknown CustomThemeStepFragment");
- }
- }
-
- private void applyTheme(ThemeBundle themeToApply) {
- mThemeManager.apply(themeToApply, new Callback() {
- @Override
- public void onSuccess() {
- overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- Toast.makeText(getApplicationContext(), R.string.applied_theme_msg,
- Toast.LENGTH_LONG).show();
- setResult(RESULT_THEME_APPLIED);
- finish();
- }
-
- @Override
- public void onError(@Nullable Throwable throwable) {
- Log.w(TAG, "Error applying custom theme", throwable);
- Toast.makeText(CustomThemeActivity.this,
- R.string.apply_theme_error_msg,
- Toast.LENGTH_LONG).show();
- }
- });
- }
-
- private CustomThemeStepFragment getCurrentStepFragment() {
- return (CustomThemeStepFragment)
- getSupportFragmentManager().findFragmentById(R.id.fragment_container);
- }
-
- @Override
- public void setCurrentStep(int i) {
- mCurrentStep = i;
- updateNavigationButtonLabels();
- }
-
- private void updateNavigationButtonLabels() {
- mPreviousButton.setVisibility(mCurrentStep == 0 ? View.INVISIBLE : View.VISIBLE);
- mNextButton.setText((mCurrentStep < mSteps.size() -1) ? R.string.custom_theme_next
- : R.string.apply_btn);
- }
-
- @Override
- public void delete() {
- mThemeManager.removeCustomTheme(mCustomThemeManager.getOriginalTheme());
- setResult(RESULT_THEME_DELETED);
- finish();
- }
-
- @Override
- public void cancel() {
- finish();
- }
-
- @Override
- public ThemeComponentOptionProvider<? extends ThemeComponentOption> getComponentOptionProvider(
- int position) {
- return mSteps.get(position).provider;
- }
-
- @Override
- public CustomThemeManager getCustomThemeManager() {
- return mCustomThemeManager;
- }
-
- @Override
- public void onUpArrowPressed() {
- // Skip it because CustomThemeStepFragment will implement cancel button
- // (instead of up arrow) on action bar.
- }
-
- @Override
- public boolean isUpArrowSupported() {
- // Skip it because CustomThemeStepFragment will implement cancel button
- // (instead of up arrow) on action bar.
- return false;
- }
-
- /**
- * Represents a step in selecting a custom theme, picking a particular component (eg font,
- * color, shape, etc).
- * Each step has a Fragment instance associated that instances of this class will provide.
- */
- private static abstract class ComponentStep<T extends ThemeComponentOption> {
- @StringRes final int titleResId;
- @StringRes final int accessibilityResId;
- final ThemeComponentOptionProvider<T> provider;
- final int position;
- private CustomThemeStepFragment mFragment;
-
- protected ComponentStep(@StringRes int titleResId, @StringRes int accessibilityResId,
- ThemeComponentOptionProvider<T> provider, int position) {
- this.titleResId = titleResId;
- this.accessibilityResId = accessibilityResId;
- this.provider = provider;
- this.position = position;
- }
-
- CustomThemeStepFragment getFragment(String title) {
- if (mFragment == null) {
- mFragment = createFragment(title);
- }
- return mFragment;
- }
-
- /**
- * @return a newly created fragment that will handle this step's UI.
- */
- abstract CustomThemeStepFragment createFragment(String title);
- }
-
- private class FontStep extends ComponentStep<FontOption> {
-
- protected FontStep(ThemeComponentOptionProvider<FontOption> provider,
- int position) {
- super(R.string.font_component_title, R.string.accessibility_custom_font_title, provider,
- position);
- }
-
- @Override
- CustomThemeComponentFragment createFragment(String title) {
- return CustomThemeComponentFragment.newInstance(
- title,
- position,
- titleResId,
- accessibilityResId);
- }
- }
-
- private class IconStep extends ComponentStep<IconOption> {
-
- protected IconStep(ThemeComponentOptionProvider<IconOption> provider,
- int position) {
- super(R.string.icon_component_title, R.string.accessibility_custom_icon_title, provider,
- position);
- }
-
- @Override
- CustomThemeComponentFragment createFragment(String title) {
- return CustomThemeComponentFragment.newInstance(
- title,
- position,
- titleResId,
- accessibilityResId);
- }
- }
-
- private class ColorStep extends ComponentStep<ColorOption> {
-
- protected ColorStep(ThemeComponentOptionProvider<ColorOption> provider,
- int position) {
- super(R.string.color_component_title, R.string.accessibility_custom_color_title,
- provider, position);
- }
-
- @Override
- CustomThemeComponentFragment createFragment(String title) {
- return CustomThemeComponentFragment.newInstance(
- title,
- position,
- titleResId,
- accessibilityResId);
- }
- }
-
- private class ShapeStep extends ComponentStep<ShapeOption> {
-
- protected ShapeStep(ThemeComponentOptionProvider<ShapeOption> provider,
- int position) {
- super(R.string.shape_component_title, R.string.accessibility_custom_shape_title,
- provider, position);
- }
-
- @Override
- CustomThemeComponentFragment createFragment(String title) {
- return CustomThemeComponentFragment.newInstance(
- title,
- position,
- titleResId,
- accessibilityResId);
- }
- }
-
- private class NameStep extends ComponentStep {
-
- protected NameStep(int position) {
- super(R.string.name_component_title, R.string.accessibility_custom_name_title, null,
- position);
- }
-
- @Override
- CustomThemeNameFragment createFragment(String title) {
- return CustomThemeNameFragment.newInstance(
- title,
- position,
- titleResId,
- accessibilityResId);
- }
- }
-}
diff --git a/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java b/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java
deleted file mode 100644
index a1e99677..00000000
--- a/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java
+++ /dev/null
@@ -1,121 +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.theme;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.customization.model.theme.custom.ThemeComponentOption;
-import com.android.customization.model.theme.custom.ThemeComponentOptionProvider;
-import com.android.customization.widget.OptionSelectorController;
-import com.android.customization.widget.OptionSelectorController.CheckmarkStyle;
-import com.android.wallpaper.R;
-import com.android.wallpaper.picker.AppbarFragment;
-
-public class CustomThemeComponentFragment extends CustomThemeStepFragment {
- private static final String ARG_USE_GRID_LAYOUT = "CustomThemeComponentFragment.use_grid";;
-
- public static CustomThemeComponentFragment newInstance(CharSequence toolbarTitle, int position,
- int titleResId, int accessibilityResId) {
- return newInstance(toolbarTitle, position, titleResId, accessibilityResId, false);
- }
-
- public static CustomThemeComponentFragment newInstance(CharSequence toolbarTitle, int position,
- int titleResId, int accessibilityResId, boolean allowGridLayout) {
- CustomThemeComponentFragment fragment = new CustomThemeComponentFragment();
- Bundle arguments = AppbarFragment.createArguments(toolbarTitle);
- arguments.putInt(ARG_KEY_POSITION, position);
- arguments.putInt(ARG_KEY_TITLE_RES_ID, titleResId);
- arguments.putInt(ARG_KEY_ACCESSIBILITY_RES_ID, accessibilityResId);
- arguments.putBoolean(ARG_USE_GRID_LAYOUT, allowGridLayout);
- fragment.setArguments(arguments);
- return fragment;
- }
-
- private ThemeComponentOptionProvider<? extends ThemeComponentOption> mProvider;
- private boolean mUseGridLayout;
-
- private RecyclerView mOptionsContainer;
- private OptionSelectorController<ThemeComponentOption> mOptionsController;
- private ThemeComponentOption mSelectedOption;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mUseGridLayout = getArguments().getBoolean(ARG_USE_GRID_LAYOUT);
- mProvider = mHost.getComponentOptionProvider(mPosition);
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- View view = super.onCreateView(inflater, container, savedInstanceState);
- mOptionsContainer = view.findViewById(R.id.options_container);
- mPreviewContainer = view.findViewById(R.id.component_preview_content);
- mTitle = view.findViewById(R.id.component_options_title);
- mTitle.setText(mTitleResId);
- setUpOptions();
-
- return view;
- }
-
- @Override
- protected int getFragmentLayoutResId() {
- return R.layout.fragment_custom_theme_component;
- }
-
- public ThemeComponentOption getSelectedOption() {
- return mSelectedOption;
- }
-
- private void bindPreview() {
- mSelectedOption.bindPreview(mPreviewContainer);
- }
-
- private void setUpOptions() {
- mProvider.fetch(options -> {
- mOptionsController = new OptionSelectorController(
- mOptionsContainer, options, mUseGridLayout, CheckmarkStyle.NONE);
-
- mOptionsController.addListener(selected -> {
- mSelectedOption = (ThemeComponentOption) selected;
- bindPreview();
- // Preview and apply. The selection will be kept whatever user goes to previous page
- // or encounter system config changes, the current selection can be recovered.
- mCustomThemeManager.apply(mSelectedOption, /* callback= */ null);
- });
- mOptionsController.initOptions(mCustomThemeManager);
-
- for (ThemeComponentOption option : options) {
- if (option.isActive(mCustomThemeManager)) {
- mSelectedOption = option;
- break;
- }
- }
- if (mSelectedOption == null) {
- mSelectedOption = options.get(0);
- }
- mOptionsController.setSelectedOption(mSelectedOption);
- }, false);
- }
-}
diff --git a/src/com/android/customization/picker/theme/CustomThemeNameFragment.java b/src/com/android/customization/picker/theme/CustomThemeNameFragment.java
deleted file mode 100644
index ea9099fc..00000000
--- a/src/com/android/customization/picker/theme/CustomThemeNameFragment.java
+++ /dev/null
@@ -1,132 +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.theme;
-
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.ImageView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
-import com.android.customization.model.theme.custom.CustomTheme;
-import com.android.customization.module.CustomizationInjector;
-import com.android.customization.module.CustomizationPreferences;
-import com.android.customization.picker.WallpaperPreviewer;
-import com.android.wallpaper.R;
-import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
-import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.picker.AppbarFragment;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-
-/** Fragment of naming a custom theme. */
-public class CustomThemeNameFragment extends CustomThemeStepFragment {
-
- private static final String TAG = "CustomThemeNameFragment";
-
- public static CustomThemeNameFragment newInstance(CharSequence toolbarTitle, int position,
- int titleResId, int accessibilityResId) {
- CustomThemeNameFragment fragment = new CustomThemeNameFragment();
- Bundle arguments = AppbarFragment.createArguments(toolbarTitle);
- arguments.putInt(ARG_KEY_POSITION, position);
- arguments.putInt(ARG_KEY_TITLE_RES_ID, titleResId);
- arguments.putInt(ARG_KEY_ACCESSIBILITY_RES_ID, accessibilityResId);
- fragment.setArguments(arguments);
- return fragment;
- }
-
- private EditText mNameEditor;
- private ImageView mWallpaperImage;
- private ThemeOptionPreviewer mThemeOptionPreviewer;
- private CustomizationPreferences mCustomizationPreferences;
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- View view = super.onCreateView(inflater, container, savedInstanceState);
- mTitle = view.findViewById(R.id.component_options_title);
- mTitle.setText(mTitleResId);
- CurrentWallpaperInfoFactory currentWallpaperFactory = InjectorProvider.getInjector()
- .getCurrentWallpaperInfoFactory(getActivity().getApplicationContext());
- CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
- mCustomizationPreferences = injector.getCustomizationPreferences(getContext());
-
- // Set theme option.
- ViewGroup previewContainer = view.findViewById(R.id.theme_preview_container);
- mThemeOptionPreviewer = new ThemeOptionPreviewer(getLifecycle(), getContext(),
- previewContainer);
- PreviewInfo previewInfo = mCustomThemeManager.buildCustomThemePreviewInfo(getContext());
- mThemeOptionPreviewer.setPreviewInfo(previewInfo);
-
- // Set wallpaper background.
- mWallpaperImage = view.findViewById(R.id.wallpaper_preview_image);
- final WallpaperPreviewer wallpaperPreviewer = new WallpaperPreviewer(
- getLifecycle(),
- getActivity(),
- mWallpaperImage,
- view.findViewById(R.id.wallpaper_preview_surface));
- currentWallpaperFactory.createCurrentWallpaperInfos(
- (homeWallpaper, lockWallpaper, presentationMode) -> {
- wallpaperPreviewer.setWallpaper(homeWallpaper,
- mThemeOptionPreviewer::updateColorForLauncherWidgets);
- }, false);
-
- // Set theme default name.
- mNameEditor = view.findViewById(R.id.custom_theme_name);
- mNameEditor.setText(getOriginalThemeName());
- return view;
- }
-
- private String getOriginalThemeName() {
- CustomTheme originalTheme = mCustomThemeManager.getOriginalTheme();
- if (originalTheme == null || !originalTheme.isDefined()) {
- // For new custom theme. use custom themes amount plus 1 as default naming.
- String serializedThemes = mCustomizationPreferences.getSerializedCustomThemes();
- int customThemesCount = 0;
- if (!TextUtils.isEmpty(serializedThemes)) {
- try {
- JSONArray customThemes = new JSONArray(serializedThemes);
- customThemesCount = customThemes.length();
- } catch (JSONException e) {
- Log.w(TAG, "Couldn't read stored custom theme");
- }
- }
- return getContext().getString(
- R.string.custom_theme_title, customThemesCount + 1);
- } else {
- // For existing custom theme, keep its name as default naming.
- return originalTheme.getTitle();
- }
- }
-
- @Override
- protected int getFragmentLayoutResId() {
- return R.layout.fragment_custom_theme_name;
- }
-
- public String getThemeName() {
- return mNameEditor.getText().toString();
- }
-}
diff --git a/src/com/android/customization/picker/theme/CustomThemeStepFragment.java b/src/com/android/customization/picker/theme/CustomThemeStepFragment.java
deleted file mode 100644
index 3f07431d..00000000
--- a/src/com/android/customization/picker/theme/CustomThemeStepFragment.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package com.android.customization.picker.theme;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-
-import com.android.customization.model.theme.custom.CustomThemeManager;
-import com.android.customization.model.theme.custom.ThemeComponentOption;
-import com.android.customization.model.theme.custom.ThemeComponentOptionProvider;
-import com.android.wallpaper.R;
-import com.android.wallpaper.picker.AppbarFragment;
-
-abstract class CustomThemeStepFragment extends AppbarFragment {
- protected static final String ARG_KEY_POSITION = "CustomThemeStepFragment.position";
- protected static final String ARG_KEY_TITLE_RES_ID = "CustomThemeStepFragment.title_res";
- protected static final String ARG_KEY_ACCESSIBILITY_RES_ID =
- "CustomThemeStepFragment.accessibility_res";
- protected CustomThemeComponentStepHost mHost;
- protected CustomThemeManager mCustomThemeManager;
- protected int mPosition;
- protected ViewGroup mPreviewContainer;
- protected TextView mTitle;
- @StringRes
- protected int mTitleResId;
- @StringRes
- protected int mAccessibilityResId;
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- mHost = (CustomThemeComponentStepHost) context;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mHost.setCurrentStep(mPosition);
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mPosition = getArguments().getInt(ARG_KEY_POSITION);
- mTitleResId = getArguments().getInt(ARG_KEY_TITLE_RES_ID);
- mAccessibilityResId = getArguments().getInt(ARG_KEY_ACCESSIBILITY_RES_ID);
- mCustomThemeManager = mHost.getCustomThemeManager();
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- View view = inflater.inflate(
- getFragmentLayoutResId(), container, /* attachToRoot */ false);
- // No original theme means it's a new one, so no toolbar icon for deleting it is needed
- if (mCustomThemeManager.getOriginalTheme() == null
- || !mCustomThemeManager.getOriginalTheme().isDefined()) {
- setUpToolbar(view);
- } else {
- setUpToolbar(view, R.menu.custom_theme_editor_menu);
- mToolbar.getMenu().getItem(0).setIconTintList(
- getContext().getColorStateList(R.color.toolbar_icon_tint));
- }
- Drawable closeIcon = getResources().getDrawable(R.drawable.ic_close_24px, null).mutate();
- closeIcon.setTintList(getResources().getColorStateList(R.color.toolbar_icon_tint, null));
- mToolbar.setNavigationIcon(closeIcon);
-
- mToolbar.setNavigationContentDescription(R.string.cancel);
- mToolbar.setNavigationOnClickListener(v -> mHost.cancel());
-
- mPreviewContainer = view.findViewById(R.id.component_preview_content);
- return view;
- }
-
- @Override
- protected String getAccessibilityTitle() {
- return getString(mAccessibilityResId);
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- if (item.getItemId() == R.id.custom_theme_delete) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
- builder.setMessage(R.string.delete_custom_theme_confirmation)
- .setPositiveButton(R.string.delete_custom_theme_button,
- (dialogInterface, i) -> mHost.delete())
- .setNegativeButton(R.string.cancel, null)
- .create()
- .show();
- return true;
- }
- return super.onMenuItemClick(item);
- }
-
- protected abstract int getFragmentLayoutResId();
-
- public interface CustomThemeComponentStepHost {
- void delete();
- void cancel();
- ThemeComponentOptionProvider<? extends ThemeComponentOption> getComponentOptionProvider(
- int position);
-
- CustomThemeManager getCustomThemeManager();
-
- void setCurrentStep(int step);
- }
-}
diff --git a/src/com/android/customization/picker/theme/ThemeFragment.java b/src/com/android/customization/picker/theme/ThemeFragment.java
deleted file mode 100644
index 3a9a56f5..00000000
--- a/src/com/android/customization/picker/theme/ThemeFragment.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (C) 2018 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.theme;
-
-import static android.app.Activity.RESULT_OK;
-
-import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
-import static com.android.wallpaper.widget.BottomActionBar.BottomAction.CUSTOMIZE;
-import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.widget.ContentLoadingProgressBar;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.customization.model.CustomizationManager.Callback;
-import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
-import com.android.customization.model.CustomizationOption;
-import com.android.customization.model.theme.ThemeBundle;
-import com.android.customization.model.theme.ThemeManager;
-import com.android.customization.model.theme.custom.CustomTheme;
-import com.android.customization.module.ThemesUserEventLogger;
-import com.android.customization.picker.WallpaperPreviewer;
-import com.android.customization.widget.OptionSelectorController;
-import com.android.wallpaper.R;
-import com.android.wallpaper.model.WallpaperInfo;
-import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
-import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.picker.AppbarFragment;
-import com.android.wallpaper.widget.BottomActionBar;
-import com.android.wallpaper.widget.BottomActionBar.AccessibilityCallback;
-import com.android.wallpaper.widget.BottomActionBar.BottomSheetContent;
-
-import java.util.List;
-
-/**
- * Fragment that contains the main UI for selecting and applying a ThemeBundle.
- */
-public class ThemeFragment extends AppbarFragment {
-
- private static final String TAG = "ThemeFragment";
- private static final String KEY_SELECTED_THEME = "ThemeFragment.SelectedThemeBundle";
- private static final String KEY_STATE_BOTTOM_ACTION_BAR_VISIBLE =
- "ThemeFragment.bottomActionBarVisible";
- private static final int FULL_PREVIEW_REQUEST_CODE = 1000;
-
- /**
- * Interface to be implemented by an Activity hosting a {@link ThemeFragment}
- */
- public interface ThemeFragmentHost {
- ThemeManager getThemeManager();
- }
- public static ThemeFragment newInstance(CharSequence title) {
- ThemeFragment fragment = new ThemeFragment();
- fragment.setArguments(AppbarFragment.createArguments(title));
- return fragment;
- }
-
- private RecyclerView mOptionsContainer;
- private OptionSelectorController<ThemeBundle> mOptionsController;
- private ThemeManager mThemeManager;
- private ThemesUserEventLogger mEventLogger;
- private ThemeBundle mSelectedTheme;
- private ContentLoadingProgressBar mLoading;
- private View mContent;
- private View mError;
- private WallpaperInfo mCurrentHomeWallpaper;
- private CurrentWallpaperInfoFactory mCurrentWallpaperFactory;
- private BottomActionBar mBottomActionBar;
- private WallpaperPreviewer mWallpaperPreviewer;
- private ImageView mWallpaperImage;
- private ThemeOptionPreviewer mThemeOptionPreviewer;
- private ThemeInfoView mThemeInfoView;
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- mThemeManager = ((ThemeFragmentHost) context).getThemeManager();
- mEventLogger = (ThemesUserEventLogger)
- InjectorProvider.getInjector().getUserEventLogger(context);
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- View view = inflater.inflate(
- R.layout.fragment_theme_picker, container, /* attachToRoot */ false);
- setUpToolbar(view);
-
- mContent = view.findViewById(R.id.content_section);
- mLoading = view.findViewById(R.id.loading_indicator);
- mError = view.findViewById(R.id.error_section);
- mCurrentWallpaperFactory = InjectorProvider.getInjector()
- .getCurrentWallpaperInfoFactory(getActivity().getApplicationContext());
- mOptionsContainer = view.findViewById(R.id.options_container);
-
- mThemeOptionPreviewer = new ThemeOptionPreviewer(
- getLifecycle(),
- getContext(),
- view.findViewById(R.id.theme_preview_container));
-
- // Set Wallpaper background.
- mWallpaperImage = view.findViewById(R.id.wallpaper_preview_image);
- mWallpaperPreviewer = new WallpaperPreviewer(
- getLifecycle(),
- getActivity(),
- mWallpaperImage,
- view.findViewById(R.id.wallpaper_preview_surface));
- mCurrentWallpaperFactory.createCurrentWallpaperInfos(
- (homeWallpaper, lockWallpaper, presentationMode) -> {
- mCurrentHomeWallpaper = homeWallpaper;
- mWallpaperPreviewer.setWallpaper(mCurrentHomeWallpaper,
- mThemeOptionPreviewer::updateColorForLauncherWidgets);
- }, false);
- return view;
- }
-
- @Override
- protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
- super.onBottomActionBarReady(bottomActionBar);
- mBottomActionBar = bottomActionBar;
- mBottomActionBar.showActionsOnly(INFORMATION, APPLY);
- mBottomActionBar.setActionClickListener(APPLY, v -> {
- mBottomActionBar.disableActions();
- applyTheme();
- });
-
- mBottomActionBar.bindBottomSheetContentWithAction(
- new ThemeInfoContent(getContext()), INFORMATION);
- mBottomActionBar.setActionClickListener(CUSTOMIZE, this::onCustomizeClicked);
-
- // Update target view's accessibility param since it will be blocked by the bottom sheet
- // when expanded.
- mBottomActionBar.setAccessibilityCallback(new AccessibilityCallback() {
- @Override
- public void onBottomSheetCollapsed() {
- mOptionsContainer.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
-
- @Override
- public void onBottomSheetExpanded() {
- mOptionsContainer.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- }
- });
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- // Setup options here when all views are ready(including BottomActionBar), since we need to
- // update views after options are loaded.
- setUpOptions(savedInstanceState);
- }
-
- private void applyTheme() {
- mThemeManager.apply(mSelectedTheme, new Callback() {
- @Override
- public void onSuccess() {
- Toast.makeText(getContext(), R.string.applied_theme_msg, Toast.LENGTH_LONG).show();
- getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- getActivity().finish();
- }
-
- @Override
- public void onError(@Nullable Throwable throwable) {
- Log.w(TAG, "Error applying theme", throwable);
- // Since we disabled it when clicked apply button.
- mBottomActionBar.enableActions();
- mBottomActionBar.hide();
- Toast.makeText(getContext(), R.string.apply_theme_error_msg,
- Toast.LENGTH_LONG).show();
- }
- });
- }
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle outState) {
- super.onSaveInstanceState(outState);
- if (mSelectedTheme != null && !mSelectedTheme.isActive(mThemeManager)) {
- outState.putString(KEY_SELECTED_THEME, mSelectedTheme.getSerializedPackages());
- }
- if (mBottomActionBar != null) {
- outState.putBoolean(KEY_STATE_BOTTOM_ACTION_BAR_VISIBLE, mBottomActionBar.isVisible());
- }
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == CustomThemeActivity.REQUEST_CODE_CUSTOM_THEME) {
- if (resultCode == CustomThemeActivity.RESULT_THEME_DELETED) {
- mSelectedTheme = null;
- reloadOptions();
- } else if (resultCode == CustomThemeActivity.RESULT_THEME_APPLIED) {
- getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- getActivity().finish();
- } else {
- if (mSelectedTheme != null) {
- mOptionsController.setSelectedOption(mSelectedTheme);
- // Set selected option above will show BottomActionBar,
- // hide BottomActionBar for the mis-trigger.
- mBottomActionBar.hide();
- } else {
- reloadOptions();
- }
- }
- } else if (requestCode == FULL_PREVIEW_REQUEST_CODE && resultCode == RESULT_OK) {
- applyTheme();
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- private void onCustomizeClicked(View view) {
- if (mSelectedTheme instanceof CustomTheme) {
- navigateToCustomTheme((CustomTheme) mSelectedTheme);
- }
- }
-
- private void hideError() {
- mContent.setVisibility(View.VISIBLE);
- mError.setVisibility(View.GONE);
- }
-
- private void showError() {
- mLoading.hide();
- mContent.setVisibility(View.GONE);
- mError.setVisibility(View.VISIBLE);
- }
-
- private void setUpOptions(@Nullable Bundle savedInstanceState) {
- hideError();
- mLoading.show();
- mThemeManager.fetchOptions(new OptionsFetchedListener<ThemeBundle>() {
- @Override
- public void onOptionsLoaded(List<ThemeBundle> options) {
- mOptionsController = new OptionSelectorController<>(mOptionsContainer, options);
- mOptionsController.initOptions(mThemeManager);
-
- // Find out the selected theme option.
- // 1. Find previously selected theme.
- String previouslySelected = savedInstanceState != null
- ? savedInstanceState.getString(KEY_SELECTED_THEME) : null;
- ThemeBundle previouslySelectedTheme = null;
- ThemeBundle activeTheme = null;
- for (ThemeBundle theme : options) {
- if (previouslySelected != null
- && previouslySelected.equals(theme.getSerializedPackages())) {
- previouslySelectedTheme = theme;
- }
- if (theme.isActive(mThemeManager)) {
- activeTheme = theme;
- }
- }
- // 2. Use active theme if no previously selected theme.
- mSelectedTheme = previouslySelectedTheme != null
- ? previouslySelectedTheme
- : activeTheme;
- // 3. Select the first system theme(default theme currently)
- // if there is no matching custom enabled theme.
- if (mSelectedTheme == null) {
- mSelectedTheme = findFirstSystemThemeBundle(options);
- }
-
- mOptionsController.setSelectedOption(mSelectedTheme);
- onOptionSelected(mSelectedTheme);
- restoreBottomActionBarVisibility(savedInstanceState);
-
- mOptionsController.addListener(selectedOption -> {
- onOptionSelected(selectedOption);
- if (!isAddCustomThemeOption(selectedOption)) {
- mBottomActionBar.show();
- }
- });
- mLoading.hide();
- }
- @Override
- public void onError(@Nullable Throwable throwable) {
- if (throwable != null) {
- Log.e(TAG, "Error loading theme bundles", throwable);
- }
- showError();
- }
- }, false);
- }
-
- private void reloadOptions() {
- mThemeManager.fetchOptions(options -> {
- mOptionsController.resetOptions(options);
- for (ThemeBundle theme : options) {
- if (theme.isActive(mThemeManager)) {
- mSelectedTheme = theme;
- break;
- }
- }
- if (mSelectedTheme == null) {
- mSelectedTheme = findFirstSystemThemeBundle(options);
- }
- mOptionsController.setSelectedOption(mSelectedTheme);
- // Set selected option above will show BottomActionBar,
- // hide BottomActionBar for the mis-trigger.
- mBottomActionBar.hide();
- }, true);
- }
-
- private ThemeBundle findFirstSystemThemeBundle(List<ThemeBundle> options) {
- for (ThemeBundle bundle : options) {
- if (!(bundle instanceof CustomTheme)) {
- return bundle;
- }
- }
- return null;
- }
-
- private void onOptionSelected(CustomizationOption selectedOption) {
- if (isAddCustomThemeOption(selectedOption)) {
- navigateToCustomTheme((CustomTheme) selectedOption);
- } else {
- mSelectedTheme = (ThemeBundle) selectedOption;
- mSelectedTheme.setOverrideThemeWallpaper(mCurrentHomeWallpaper);
- mEventLogger.logThemeSelected(mSelectedTheme,
- selectedOption instanceof CustomTheme);
- mThemeOptionPreviewer.setPreviewInfo(mSelectedTheme.getPreviewInfo());
- if (mThemeInfoView != null && mSelectedTheme != null) {
- mThemeInfoView.populateThemeInfo(mSelectedTheme);
- }
-
- if (selectedOption instanceof CustomTheme) {
- mBottomActionBar.showActionsOnly(INFORMATION, CUSTOMIZE, APPLY);
- } else {
- mBottomActionBar.showActionsOnly(INFORMATION, APPLY);
- }
- }
- }
-
- private void restoreBottomActionBarVisibility(@Nullable Bundle savedInstanceState) {
- boolean isBottomActionBarVisible = savedInstanceState != null
- && savedInstanceState.getBoolean(KEY_STATE_BOTTOM_ACTION_BAR_VISIBLE);
- if (isBottomActionBarVisible) {
- mBottomActionBar.show();
- } else {
- mBottomActionBar.hide();
- }
- }
-
- private boolean isAddCustomThemeOption(CustomizationOption option) {
- return option instanceof CustomTheme && !((CustomTheme) option).isDefined();
- }
-
- private void navigateToCustomTheme(CustomTheme themeToEdit) {
- Intent intent = new Intent(getActivity(), CustomThemeActivity.class);
- intent.putExtra(CustomThemeActivity.EXTRA_THEME_TITLE, themeToEdit.getTitle());
- intent.putExtra(CustomThemeActivity.EXTRA_THEME_ID, themeToEdit.getId());
- intent.putExtra(CustomThemeActivity.EXTRA_THEME_PACKAGES,
- themeToEdit.getSerializedPackages());
- startActivityForResult(intent, CustomThemeActivity.REQUEST_CODE_CUSTOM_THEME);
- }
-
- private final class ThemeInfoContent extends BottomSheetContent<ThemeInfoView> {
-
- private ThemeInfoContent(Context context) {
- super(context);
- }
-
- @Override
- public int getViewId() {
- return R.layout.theme_info_view;
- }
-
- @Override
- public void onViewCreated(ThemeInfoView view) {
- mThemeInfoView = view;
- if (mSelectedTheme != null) {
- mThemeInfoView.populateThemeInfo(mSelectedTheme);
- }
- }
- }
-}
diff --git a/src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java b/src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java
deleted file mode 100644
index 3ba64ecc..00000000
--- a/src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2020 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.theme;
-
-import static android.app.Activity.RESULT_OK;
-
-import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
-import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.customization.model.theme.DefaultThemeProvider;
-import com.android.customization.model.theme.ThemeBundle;
-import com.android.customization.model.theme.ThemeBundleProvider;
-import com.android.customization.module.CustomizationInjector;
-import com.android.customization.picker.WallpaperPreviewer;
-import com.android.wallpaper.R;
-import com.android.wallpaper.model.WallpaperInfo;
-import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.picker.AppbarFragment;
-import com.android.wallpaper.widget.BottomActionBar;
-import com.android.wallpaper.widget.BottomActionBar.BottomSheetContent;
-
-import com.bumptech.glide.Glide;
-
-import org.json.JSONException;
-
-/** A Fragment for theme full preview page. */
-public class ThemeFullPreviewFragment extends AppbarFragment {
- private static final String TAG = "ThemeFullPreviewFragment";
-
- public static final String EXTRA_THEME_OPTION_TITLE = "theme_option_title";
- protected static final String EXTRA_THEME_OPTION = "theme_option";
- protected static final String EXTRA_WALLPAPER_INFO = "wallpaper_info";
- protected static final String EXTRA_CAN_APPLY_FROM_FULL_PREVIEW = "can_apply";
-
- private WallpaperInfo mWallpaper;
- private ThemeBundle mThemeBundle;
- private boolean mCanApplyFromFullPreview;
-
- /**
- * Returns a new {@link ThemeFullPreviewFragment} with the provided title and bundle arguments
- * set.
- */
- public static ThemeFullPreviewFragment newInstance(CharSequence title, Bundle intentBundle) {
- ThemeFullPreviewFragment fragment = new ThemeFullPreviewFragment();
- Bundle bundle = new Bundle();
- bundle.putAll(AppbarFragment.createArguments(title));
- bundle.putAll(intentBundle);
- fragment.setArguments(bundle);
- return fragment;
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mWallpaper = getArguments().getParcelable(EXTRA_WALLPAPER_INFO);
- mCanApplyFromFullPreview = getArguments().getBoolean(EXTRA_CAN_APPLY_FROM_FULL_PREVIEW);
- CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
- ThemeBundleProvider themeProvider = new DefaultThemeProvider(
- getContext(), injector.getCustomizationPreferences(getContext()));
- try {
- ThemeBundle.Builder builder = themeProvider.parseThemeBundle(
- getArguments().getString(EXTRA_THEME_OPTION));
- if (builder != null) {
- builder.setTitle(getArguments().getString(EXTRA_THEME_OPTION_TITLE));
- mThemeBundle = builder.build(getContext());
- }
- } catch (JSONException e) {
- Log.w(TAG, "Couldn't parse provided custom theme, will override it");
- // TODO(chihhangchuang): Handle the error case.
- }
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- View view = inflater.inflate(
- R.layout.fragment_theme_full_preview, container, /* attachToRoot */ false);
- setUpToolbar(view);
- Glide.get(getContext()).clearMemory();
-
- // Set theme option.
- final ThemeOptionPreviewer themeOptionPreviewer = new ThemeOptionPreviewer(
- getLifecycle(),
- getContext(),
- view.findViewById(R.id.theme_preview_container));
- themeOptionPreviewer.setPreviewInfo(mThemeBundle.getPreviewInfo());
-
- // Set wallpaper background.
- ImageView wallpaperImageView = view.findViewById(R.id.wallpaper_preview_image);
- final WallpaperPreviewer wallpaperPreviewer = new WallpaperPreviewer(
- getLifecycle(),
- getActivity(),
- wallpaperImageView,
- view.findViewById(R.id.wallpaper_preview_surface));
- wallpaperPreviewer.setWallpaper(mWallpaper,
- themeOptionPreviewer::updateColorForLauncherWidgets);
- return view;
- }
-
- @Override
- protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
- super.onBottomActionBarReady(bottomActionBar);
- if (mCanApplyFromFullPreview) {
- bottomActionBar.showActionsOnly(INFORMATION, APPLY);
- bottomActionBar.setActionClickListener(APPLY, v -> finishActivityWithResultOk());
- } else {
- bottomActionBar.showActionsOnly(INFORMATION);
- }
- bottomActionBar.bindBottomSheetContentWithAction(
- new ThemeInfoContent(getContext()), INFORMATION);
- bottomActionBar.show();
- }
-
- private void finishActivityWithResultOk() {
- Activity activity = requireActivity();
- activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- Intent intent = new Intent();
- activity.setResult(RESULT_OK, intent);
- activity.finish();
- }
-
- private final class ThemeInfoContent extends BottomSheetContent<ThemeInfoView> {
-
- private ThemeInfoContent(Context context) {
- super(context);
- }
-
- @Override
- public int getViewId() {
- return R.layout.theme_info_view;
- }
-
- @Override
- public void onViewCreated(ThemeInfoView view) {
- view.populateThemeInfo(mThemeBundle);
- }
- }
-}
diff --git a/src/com/android/customization/picker/theme/ThemeInfoView.java b/src/com/android/customization/picker/theme/ThemeInfoView.java
deleted file mode 100644
index e929c4d2..00000000
--- a/src/com/android/customization/picker/theme/ThemeInfoView.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2020 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.theme;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.customization.model.theme.ThemeBundle;
-import com.android.wallpaper.R;
-
-/** A view for displaying style info. */
-public class ThemeInfoView extends LinearLayout {
- private static final int WIFI_ICON_PREVIEW_INDEX = 0;
- private static final int SHAPE_PREVIEW_INDEX = 0;
-
- private TextView mTitle;
- private TextView mFontPreviewTextView;
- private ImageView mIconPreviewImageView;
- private ImageView mAppPreviewImageView;
- private ImageView mShapePreviewImageView;
-
- public ThemeInfoView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mTitle = findViewById(R.id.style_info_title);
- mFontPreviewTextView = findViewById(R.id.font_preview);
- mIconPreviewImageView = findViewById(R.id.qs_preview_icon);
- mAppPreviewImageView = findViewById(R.id.app_preview_icon);
- mShapePreviewImageView = findViewById(R.id.shape_preview_icon);
- }
-
- /** Populates theme info. */
- public void populateThemeInfo(@NonNull ThemeBundle selectedTheme) {
- ThemeBundle.PreviewInfo previewInfo = selectedTheme.getPreviewInfo();
-
- if (previewInfo != null) {
- mTitle.setText(getContext().getString(R.string.style_info_description));
- if (previewInfo.headlineFontFamily != null) {
- mTitle.setTypeface(previewInfo.headlineFontFamily);
- mFontPreviewTextView.setTypeface(previewInfo.headlineFontFamily);
- }
-
- if (previewInfo.icons.get(WIFI_ICON_PREVIEW_INDEX) != null) {
- mIconPreviewImageView.setImageDrawable(
- previewInfo.icons.get(WIFI_ICON_PREVIEW_INDEX));
- }
-
- if (previewInfo.shapeAppIcons.get(SHAPE_PREVIEW_INDEX) != null) {
- mAppPreviewImageView.setBackground(
- previewInfo.shapeAppIcons.get(SHAPE_PREVIEW_INDEX).getDrawableCopy());
- }
-
- if (previewInfo.shapeDrawable != null) {
- mShapePreviewImageView.setImageDrawable(previewInfo.shapeDrawable);
- mShapePreviewImageView.setImageTintList(
- ColorStateList.valueOf(previewInfo.resolveAccentColor(getResources())));
- }
- }
- }
-}
diff --git a/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java b/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java
deleted file mode 100644
index 14b53ec4..00000000
--- a/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * Copyright (C) 2020 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.theme;
-
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
-import android.app.WallpaperColors;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.text.format.DateFormat;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-import androidx.cardview.widget.CardView;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleObserver;
-import androidx.lifecycle.OnLifecycleEvent;
-
-import com.android.customization.model.theme.ThemeBundle;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo.ShapeAppIcon;
-import com.android.wallpaper.R;
-import com.android.wallpaper.util.ResourceUtils;
-import com.android.wallpaper.util.ScreenSizeCalculator;
-import com.android.wallpaper.util.TimeUtils;
-import com.android.wallpaper.util.TimeUtils.TimeTicker;
-
-import java.util.Calendar;
-import java.util.List;
-import java.util.Locale;
-import java.util.TimeZone;
-
-/** A class to load the {@link ThemeBundle} preview to the view. */
-class ThemeOptionPreviewer implements LifecycleObserver {
- private static final String DATE_FORMAT = "EEEE, MMM d";
-
- // Maps which icon from ResourceConstants#ICONS_FOR_PREVIEW.
- private static final int ICON_WIFI = 0;
- private static final int ICON_BLUETOOTH = 1;
- private static final int ICON_FLASHLIGHT = 3;
- private static final int ICON_AUTO_ROTATE = 4;
- private static final int ICON_CELLULAR_SIGNAL = 6;
- private static final int ICON_BATTERY = 7;
-
- // Icons in the top bar (fake "status bar") with the particular order.
- private static final int [] sTopBarIconToPreviewIcon = new int [] {
- ICON_WIFI, ICON_CELLULAR_SIGNAL, ICON_BATTERY };
-
- // Ids of app icon shape preview.
- private int[] mShapeAppIconIds = {
- R.id.shape_preview_icon_0, R.id.shape_preview_icon_1,
- R.id.shape_preview_icon_2, R.id.shape_preview_icon_3
- };
- private int[] mShapeIconAppNameIds = {
- R.id.shape_preview_icon_app_name_0, R.id.shape_preview_icon_app_name_1,
- R.id.shape_preview_icon_app_name_2, R.id.shape_preview_icon_app_name_3
- };
-
- // Ids of color/icons section.
- private int[][] mColorTileIconIds = {
- new int[] { R.id.preview_color_qs_0_icon, ICON_WIFI},
- new int[] { R.id.preview_color_qs_1_icon, ICON_BLUETOOTH},
- new int[] { R.id.preview_color_qs_2_icon, ICON_FLASHLIGHT},
- new int[] { R.id.preview_color_qs_3_icon, ICON_AUTO_ROTATE},
- };
- private int[] mColorTileIds = {
- R.id.preview_color_qs_0_bg, R.id.preview_color_qs_1_bg,
- R.id.preview_color_qs_2_bg, R.id.preview_color_qs_3_bg
- };
- private int[] mColorButtonIds = {
- R.id.preview_check_selected, R.id.preview_radio_selected, R.id.preview_toggle_selected
- };
-
- private final Context mContext;
-
- private View mContentView;
- private TextView mStatusBarClock;
- private TextView mSmartSpaceDate;
- private TimeTicker mTicker;
-
- private boolean mHasPreviewInfoSet;
- private boolean mHasWallpaperColorSet;
-
- ThemeOptionPreviewer(Lifecycle lifecycle, Context context, ViewGroup previewContainer) {
- lifecycle.addObserver(this);
-
- mContext = context;
- mContentView = LayoutInflater.from(context).inflate(
- R.layout.theme_preview_content, /* root= */ null);
- mContentView.setVisibility(View.INVISIBLE);
- mStatusBarClock = mContentView.findViewById(R.id.theme_preview_clock);
- mSmartSpaceDate = mContentView.findViewById(R.id.smart_space_date);
- updateTime();
- final float screenAspectRatio =
- ScreenSizeCalculator.getInstance().getScreenAspectRatio(mContext);
- Configuration config = mContext.getResources().getConfiguration();
- final boolean directionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
- previewContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View view, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- // Calculate the full preview card height and width.
- final int fullPreviewCardHeight = getFullPreviewCardHeight();
- final int fullPreviewCardWidth = (int) (fullPreviewCardHeight / screenAspectRatio);
-
- // Relayout the content view to match full preview card size.
- mContentView.measure(
- makeMeasureSpec(fullPreviewCardWidth, EXACTLY),
- makeMeasureSpec(fullPreviewCardHeight, EXACTLY));
- mContentView.layout(0, 0, fullPreviewCardWidth, fullPreviewCardHeight);
-
- // Scale the content view from full preview size to the container size. For full
- // preview, the scale value is 1.
- float scale = (float) previewContainer.getMeasuredHeight() / fullPreviewCardHeight;
- mContentView.setScaleX(scale);
- mContentView.setScaleY(scale);
- // The pivot point is centered by default, set to (0, 0).
- mContentView.setPivotX(directionLTR ? 0f : mContentView.getMeasuredWidth());
- mContentView.setPivotY(0f);
-
- // Ensure there will be only one content view in the container.
- previewContainer.removeAllViews();
- // Finally, add the content view to the container.
- previewContainer.addView(
- mContentView,
- mContentView.getMeasuredWidth(),
- mContentView.getMeasuredHeight());
-
- previewContainer.removeOnLayoutChangeListener(this);
- }
- });
- }
-
- /** Loads the Theme option preview into the container view. */
- public void setPreviewInfo(PreviewInfo previewInfo) {
- setHeadlineFont(previewInfo.headlineFontFamily);
- setBodyFont(previewInfo.bodyFontFamily);
- setTopBarIcons(previewInfo.icons);
- setAppIconShape(previewInfo.shapeAppIcons);
- setColorAndIconsSection(previewInfo.icons, previewInfo.shapeDrawable,
- previewInfo.resolveAccentColor(mContext.getResources()));
- setColorAndIconsBoxRadius(previewInfo.bottomSheeetCornerRadius);
- setQsbRadius(previewInfo.bottomSheeetCornerRadius);
- mHasPreviewInfoSet = true;
- showPreviewIfHasAllConfigSet();
- }
-
- /**
- * Updates the color of widgets in launcher (like top status bar, smart space, and app name
- * text) which will change its content color according to different wallpapers.
- *
- * @param colors the {@link WallpaperColors} of the wallpaper, or {@code null} to use light
- * color as default
- */
- public void updateColorForLauncherWidgets(@Nullable WallpaperColors colors) {
- boolean useLightTextColor = colors == null
- || (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0;
- int textColor = mContext.getColor(useLightTextColor
- ? android.R.color.white
- : android.R.color.black);
- int textShadowColor = mContext.getColor(useLightTextColor
- ? android.R.color.tertiary_text_dark
- : android.R.color.transparent);
- // Update the top status bar clock text color.
- mStatusBarClock.setTextColor(textColor);
- // Update the top status bar icon color.
- ViewGroup iconsContainer = mContentView.findViewById(R.id.theme_preview_top_bar_icons);
- for (int i = 0; i < iconsContainer.getChildCount(); i++) {
- ((ImageView) iconsContainer.getChildAt(i))
- .setImageTintList(ColorStateList.valueOf(textColor));
- }
- // Update smart space date color.
- mSmartSpaceDate.setTextColor(textColor);
- mSmartSpaceDate.setShadowLayer(
- mContext.getResources().getDimension(
- R.dimen.smartspace_preview_key_ambient_shadow_blur),
- /* dx = */ 0,
- /* dy = */ 0,
- textShadowColor);
-
- // Update shape app icon name text color.
- for (int id : mShapeIconAppNameIds) {
- TextView appName = mContentView.findViewById(id);
- appName.setTextColor(textColor);
- appName.setShadowLayer(
- mContext.getResources().getDimension(
- R.dimen.preview_theme_app_name_key_ambient_shadow_blur),
- /* dx = */ 0,
- /* dy = */ 0,
- textShadowColor);
- }
-
- mHasWallpaperColorSet = true;
- showPreviewIfHasAllConfigSet();
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
- @MainThread
- public void onResume() {
- mTicker = TimeTicker.registerNewReceiver(mContext, this::updateTime);
- updateTime();
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
- @MainThread
- public void onPause() {
- if (mContext != null) {
- mContext.unregisterReceiver(mTicker);
- }
- }
-
- private void showPreviewIfHasAllConfigSet() {
- if (mHasPreviewInfoSet && mHasWallpaperColorSet
- && mContentView.getVisibility() != View.VISIBLE) {
- mContentView.setAlpha(0f);
- mContentView.setVisibility(View.VISIBLE);
- mContentView.animate().alpha(1f)
- .setStartDelay(50)
- .setDuration(200)
- .setInterpolator(AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_linear_in))
- .start();
- }
- }
-
- private void setHeadlineFont(Typeface headlineFont) {
- mStatusBarClock.setTypeface(headlineFont);
- mSmartSpaceDate.setTypeface(headlineFont);
-
- // Update font of color/icons section title.
- TextView colorIconsSectionTitle = mContentView.findViewById(R.id.color_icons_section_title);
- colorIconsSectionTitle.setTypeface(headlineFont);
- }
-
- private void setBodyFont(Typeface bodyFont) {
- // Update font of app names.
- for (int id : mShapeIconAppNameIds) {
- TextView appName = mContentView.findViewById(id);
- appName.setTypeface(bodyFont);
- }
- }
-
- private void setTopBarIcons(List<Drawable> icons) {
- ViewGroup iconsContainer = mContentView.findViewById(R.id.theme_preview_top_bar_icons);
- for (int i = 0; i < iconsContainer.getChildCount(); i++) {
- int iconIndex = sTopBarIconToPreviewIcon[i];
- if (iconIndex < icons.size()) {
- ((ImageView) iconsContainer.getChildAt(i))
- .setImageDrawable(icons.get(iconIndex).getConstantState()
- .newDrawable().mutate());
- } else {
- iconsContainer.getChildAt(i).setVisibility(View.GONE);
- }
- }
- }
-
- private void setAppIconShape(List<ShapeAppIcon> appIcons) {
- for (int i = 0; i < mShapeAppIconIds.length && i < mShapeIconAppNameIds.length
- && i < appIcons.size(); i++) {
- ShapeAppIcon icon = appIcons.get(i);
- // Set app icon.
- ImageView iconView = mContentView.findViewById(mShapeAppIconIds[i]);
- iconView.setBackground(icon.getDrawableCopy());
- // Set app name.
- TextView appName = mContentView.findViewById(mShapeIconAppNameIds[i]);
- appName.setText(icon.getAppName());
- }
- }
-
- private void setColorAndIconsSection(List<Drawable> icons, Drawable shapeDrawable,
- int accentColor) {
- // Set QS icons and background.
- for (int i = 0; i < mColorTileIconIds.length && i < icons.size(); i++) {
- Drawable icon = icons.get(mColorTileIconIds[i][1]).getConstantState()
- .newDrawable().mutate();
- Drawable bgShape = shapeDrawable.getConstantState().newDrawable();
- bgShape.setTint(accentColor);
-
- ImageView bg = mContentView.findViewById(mColorTileIds[i]);
- bg.setImageDrawable(bgShape);
- ImageView fg = mContentView.findViewById(mColorTileIconIds[i][0]);
- fg.setImageDrawable(icon);
- }
-
- // Set color for Buttons (CheckBox, RadioButton, and Switch).
- ColorStateList tintList = getColorStateList(accentColor);
- for (int mColorButtonId : mColorButtonIds) {
- CompoundButton button = mContentView.findViewById(mColorButtonId);
- button.setButtonTintList(tintList);
- if (button instanceof Switch) {
- ((Switch) button).setThumbTintList(tintList);
- ((Switch) button).setTrackTintList(tintList);
- }
- }
- }
-
- private void setColorAndIconsBoxRadius(int cornerRadius) {
- ((CardView) mContentView.findViewById(R.id.color_icons_section)).setRadius(cornerRadius);
- }
-
- private void setQsbRadius(int cornerRadius) {
- View qsb = mContentView.findViewById(R.id.theme_qsb);
- if (qsb != null && qsb.getVisibility() == View.VISIBLE) {
- if (qsb.getBackground() instanceof GradientDrawable) {
- GradientDrawable bg = (GradientDrawable) qsb.getBackground();
- float radius = useRoundedQSB(cornerRadius)
- ? (float) qsb.getLayoutParams().height / 2 : cornerRadius;
- bg.setCornerRadii(new float[]{
- radius, radius, radius, radius,
- radius, radius, radius, radius});
- }
- }
- }
-
- private void updateTime() {
- Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
- if (mStatusBarClock != null) {
- mStatusBarClock.setText(TimeUtils.getFormattedTime(mContext, calendar));
- }
- if (mSmartSpaceDate != null) {
- String datePattern =
- DateFormat.getBestDateTimePattern(Locale.getDefault(), DATE_FORMAT);
- mSmartSpaceDate.setText(DateFormat.format(datePattern, calendar));
- }
- }
-
- private boolean useRoundedQSB(int cornerRadius) {
- return cornerRadius >= mContext.getResources().getDimensionPixelSize(
- R.dimen.roundCornerThreshold);
- }
-
- private ColorStateList getColorStateList(int accentColor) {
- int controlGreyColor =
- ResourceUtils.getColorAttr(mContext, android.R.attr.textColorTertiary);
- return new ColorStateList(
- new int[][]{
- new int[]{android.R.attr.state_selected},
- new int[]{android.R.attr.state_checked},
- new int[]{-android.R.attr.state_enabled},
- },
- new int[] {
- accentColor,
- accentColor,
- controlGreyColor
- }
- );
- }
-
- /**
- * Gets the screen height which does not include the system status bar and bottom navigation
- * bar.
- */
- private int getDisplayHeight() {
- final DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
- return dm.heightPixels;
- }
-
- // The height of top tool bar (R.layout.section_header).
- private int getTopToolBarHeight() {
- final TypedValue typedValue = new TypedValue();
- return mContext.getTheme().resolveAttribute(
- android.R.attr.actionBarSize, typedValue, true)
- ? TypedValue.complexToDimensionPixelSize(
- typedValue.data, mContext.getResources().getDisplayMetrics())
- : 0;
- }
-
- private int getFullPreviewCardHeight() {
- final Resources res = mContext.getResources();
- return getDisplayHeight()
- - getTopToolBarHeight()
- - res.getDimensionPixelSize(R.dimen.bottom_actions_height)
- - res.getDimensionPixelSize(R.dimen.full_preview_page_default_padding_top)
- - res.getDimensionPixelSize(R.dimen.full_preview_page_default_padding_bottom);
- }
-}
diff --git a/src/com/android/customization/picker/themedicon/ThemedIconSectionView.java b/src/com/android/customization/picker/themedicon/ThemedIconSectionView.java
index 3e03a41c..f83da8c1 100644
--- a/src/com/android/customization/picker/themedicon/ThemedIconSectionView.java
+++ b/src/com/android/customization/picker/themedicon/ThemedIconSectionView.java
@@ -40,18 +40,16 @@ public class ThemedIconSectionView extends SectionView {
protected void onFinishInflate() {
super.onFinishInflate();
mSwitchView = findViewById(R.id.themed_icon_toggle);
- setOnClickListener(v -> mSwitchView.toggle());
- mSwitchView.setOnCheckedChangeListener((buttonView, isChecked) -> viewActivated(isChecked));
+ setOnClickListener(v -> {
+ mSwitchView.toggle();
+ if (mSectionViewListener != null) {
+ mSectionViewListener.onViewActivated(getContext(), mSwitchView.isChecked());
+ }
+ });
}
/** Gets the switch view. */
public Switch getSwitch() {
return mSwitchView;
}
-
- private void viewActivated(boolean isChecked) {
- if (mSectionViewListener != null) {
- mSectionViewListener.onViewActivated(getContext(), isChecked);
- }
- }
}
diff --git a/src/com/android/customization/widget/OptionSelectorController.java b/src/com/android/customization/widget/OptionSelectorController.java
deleted file mode 100644
index 8c7af00b..00000000
--- a/src/com/android/customization/widget/OptionSelectorController.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * Copyright (C) 2018 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.widget;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.TextView;
-
-import androidx.annotation.Dimension;
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
-
-import com.android.customization.model.CustomizationManager;
-import com.android.customization.model.CustomizationOption;
-import com.android.wallpaper.R;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Simple controller for a RecyclerView-based widget to hold the options for each customization
- * section (eg, thumbnails for themes, clocks, grid sizes).
- * To use, just pass the RV that will contain the tiles and the list of {@link CustomizationOption}
- * representing each option, and call {@link #initOptions(CustomizationManager)} to populate the
- * widget.
- */
-public class OptionSelectorController<T extends CustomizationOption<T>> {
-
- /**
- * Interface to be notified when an option is selected by the user.
- */
- public interface OptionSelectedListener {
-
- /**
- * Called when an option has been selected (and marked as such in the UI)
- */
- void onOptionSelected(CustomizationOption selected);
- }
-
- @IntDef({CheckmarkStyle.NONE, CheckmarkStyle.CORNER, CheckmarkStyle.CENTER,
- CheckmarkStyle.CENTER_CHANGE_COLOR_WHEN_NOT_SELECTED})
- public @interface CheckmarkStyle {
- int NONE = 0;
- int CORNER = 1;
- int CENTER = 2;
- int CENTER_CHANGE_COLOR_WHEN_NOT_SELECTED = 3;
- }
-
- private final float mLinearLayoutHorizontalDisplayOptionsMax;
-
- private final RecyclerView mContainer;
- private final List<T> mOptions;
- private final boolean mUseGrid;
- @CheckmarkStyle
- private final int mCheckmarkStyle;
-
- private final Set<OptionSelectedListener> mListeners = new HashSet<>();
- private RecyclerView.Adapter<TileViewHolder> mAdapter;
- private T mSelectedOption;
- private T mAppliedOption;
-
- public OptionSelectorController(RecyclerView container, List<T> options) {
- this(container, options, true, CheckmarkStyle.CORNER);
- }
-
- public OptionSelectorController(RecyclerView container, List<T> options,
- boolean useGrid, @CheckmarkStyle int checkmarkStyle) {
- mContainer = container;
- mOptions = options;
- mUseGrid = useGrid;
- mCheckmarkStyle = checkmarkStyle;
- TypedValue typedValue = new TypedValue();
- mContainer.getResources().getValue(R.dimen.linear_layout_horizontal_display_options_max,
- typedValue, true);
- mLinearLayoutHorizontalDisplayOptionsMax = typedValue.getFloat();
- }
-
- public void addListener(OptionSelectedListener listener) {
- mListeners.add(listener);
- }
-
- public void removeListener(OptionSelectedListener listener) {
- mListeners.remove(listener);
- }
-
- /**
- * Mark the given option as selected
- */
- public void setSelectedOption(T option) {
- if (!mOptions.contains(option)) {
- throw new IllegalArgumentException("Invalid option");
- }
- T lastSelectedOption = mSelectedOption;
- mSelectedOption = option;
- mAdapter.notifyItemChanged(mOptions.indexOf(option));
- if (lastSelectedOption != null) {
- mAdapter.notifyItemChanged(mOptions.indexOf(lastSelectedOption));
- }
- notifyListeners();
- }
-
- /**
- * @return whether this controller contains the given option
- */
- public boolean containsOption(T option) {
- return mOptions.contains(option);
- }
-
- /**
- * Mark an option as the one which is currently applied on the device. This will result in a
- * check being displayed in the lower-right corner of the corresponding ViewHolder.
- */
- public void setAppliedOption(T option) {
- if (!mOptions.contains(option)) {
- throw new IllegalArgumentException("Invalid option");
- }
- T lastAppliedOption = mAppliedOption;
- mAppliedOption = option;
- mAdapter.notifyItemChanged(mOptions.indexOf(option));
- if (lastAppliedOption != null) {
- mAdapter.notifyItemChanged(mOptions.indexOf(lastAppliedOption));
- }
- }
-
- /**
- * Notify that a given option has changed.
- *
- * @param option the option that changed
- */
- public void optionChanged(T option) {
- if (!mOptions.contains(option)) {
- throw new IllegalArgumentException("Invalid option");
- }
- mAdapter.notifyItemChanged(mOptions.indexOf(option));
- }
-
- /**
- * Initializes the UI for the options passed in the constructor of this class.
- */
- public void initOptions(final CustomizationManager<T> manager) {
- mContainer.setAccessibilityDelegateCompat(
- new OptionSelectorAccessibilityDelegate(mContainer));
-
- mAdapter = new RecyclerView.Adapter<TileViewHolder>() {
- @Override
- public int getItemViewType(int position) {
- return mOptions.get(position).getLayoutResId();
- }
-
- @NonNull
- @Override
- public TileViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View v = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
- // Provide width constraint when a grid layout manager is not use and width is set
- // to match parent
- if (!mUseGrid
- && v.getLayoutParams().width == RecyclerView.LayoutParams.MATCH_PARENT) {
- Resources res = mContainer.getContext().getResources();
- RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(
- res.getDimensionPixelSize(R.dimen.option_tile_width),
- RecyclerView.LayoutParams.WRAP_CONTENT);
- v.setLayoutParams(layoutParams);
- }
- return new TileViewHolder(v);
- }
-
- @Override
- public void onBindViewHolder(@NonNull TileViewHolder holder, int position) {
- T option = mOptions.get(position);
- if (option.isActive(manager)) {
- mAppliedOption = option;
- if (mSelectedOption == null) {
- mSelectedOption = option;
- }
- }
- if (holder.labelView != null) {
- holder.labelView.setText(option.getTitle());
- }
- holder.itemView.setActivated(option.equals(mSelectedOption));
- option.bindThumbnailTile(holder.tileView);
- holder.itemView.setOnClickListener(view -> setSelectedOption(option));
-
- Resources res = mContainer.getContext().getResources();
- if (mCheckmarkStyle == CheckmarkStyle.CORNER && option.equals(mAppliedOption)) {
- drawCheckmark(option, holder,
- res.getDrawable(R.drawable.check_circle_accent_24dp,
- mContainer.getContext().getTheme()),
- Gravity.BOTTOM | Gravity.RIGHT,
- res.getDimensionPixelSize(R.dimen.check_size),
- res.getDimensionPixelOffset(R.dimen.check_offset), true);
- } else if (mCheckmarkStyle == CheckmarkStyle.CENTER
- && option.equals(mAppliedOption)) {
- drawCheckmark(option, holder,
- res.getDrawable(R.drawable.check_circle_grey_large,
- mContainer.getContext().getTheme()),
- Gravity.CENTER, res.getDimensionPixelSize(R.dimen.center_check_size),
- 0, true);
- } else if (mCheckmarkStyle == CheckmarkStyle.CENTER_CHANGE_COLOR_WHEN_NOT_SELECTED
- && option.equals(mAppliedOption)) {
- int drawableRes = option.equals(mSelectedOption)
- ? R.drawable.check_circle_grey_large
- : R.drawable.check_circle_grey_large_not_select;
- drawCheckmark(option, holder,
- res.getDrawable(drawableRes,
- mContainer.getContext().getTheme()),
- Gravity.CENTER, res.getDimensionPixelSize(R.dimen.center_check_size),
- 0, option.equals(mSelectedOption));
- } else if (option.equals(mAppliedOption)) {
- // Initialize with "previewed" description if we don't show checkmark
- holder.setContentDescription(mContainer.getContext(), option,
- R.string.option_previewed_description);
- } else if (mCheckmarkStyle != CheckmarkStyle.NONE) {
- if (mCheckmarkStyle == CheckmarkStyle.CENTER_CHANGE_COLOR_WHEN_NOT_SELECTED) {
- if (option.equals(mSelectedOption)) {
- holder.setContentDescription(mContainer.getContext(), option,
- R.string.option_previewed_description);
- } else {
- holder.setContentDescription(mContainer.getContext(), option,
- R.string.option_change_applied_previewed_description);
- }
- }
-
- holder.tileView.setForeground(null);
- }
- }
-
- @Override
- public int getItemCount() {
- return mOptions.size();
- }
-
- private void drawCheckmark(CustomizationOption<?> option, TileViewHolder holder,
- Drawable checkmark, int gravity, @Dimension int checkSize,
- @Dimension int checkOffset, boolean currentlyPreviewed) {
- Drawable frame = holder.tileView.getForeground();
- Drawable[] layers = {frame, checkmark};
- if (frame == null) {
- layers = new Drawable[]{checkmark};
- }
- LayerDrawable checkedFrame = new LayerDrawable(layers);
-
- // Position according to the given gravity and offset
- int idx = layers.length - 1;
- checkedFrame.setLayerGravity(idx, gravity);
- checkedFrame.setLayerWidth(idx, checkSize);
- checkedFrame.setLayerHeight(idx, checkSize);
- checkedFrame.setLayerInsetBottom(idx, checkOffset);
- checkedFrame.setLayerInsetRight(idx, checkOffset);
- holder.tileView.setForeground(checkedFrame);
-
- // Initialize the currently applied option
- if (currentlyPreviewed) {
- holder.setContentDescription(mContainer.getContext(), option,
- R.string.option_applied_previewed_description);
- } else {
- holder.setContentDescription(mContainer.getContext(), option,
- R.string.option_applied_description);
- }
- }
- };
-
- Resources res = mContainer.getContext().getResources();
- mContainer.setAdapter(mAdapter);
- final DisplayMetrics metrics = new DisplayMetrics();
- mContainer.getContext().getSystemService(WindowManager.class)
- .getDefaultDisplay().getMetrics(metrics);
- final boolean hasDecoration = mContainer.getItemDecorationCount() != 0;
-
- if (mUseGrid) {
- int numColumns = res.getInteger(R.integer.options_grid_num_columns);
- GridLayoutManager gridLayoutManager = new GridLayoutManager(mContainer.getContext(),
- numColumns);
- mContainer.setLayoutManager(gridLayoutManager);
- } else {
- final int padding = res.getDimensionPixelSize(
- R.dimen.option_tile_linear_padding_horizontal);
- final int widthPerItem = res.getDimensionPixelSize(R.dimen.option_tile_width) + (
- hasDecoration ? 0 : 2 * padding);
- mContainer.setLayoutManager(new LinearLayoutManager(mContainer.getContext(),
- LinearLayoutManager.HORIZONTAL, false));
- mContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- int availableWidth = metrics.widthPixels;
- int extraSpace = availableWidth - mContainer.getMeasuredWidth();
- if (extraSpace >= 0) {
- mContainer.setOverScrollMode(View.OVER_SCROLL_NEVER);
- }
-
- if (mAdapter.getItemCount() >= mLinearLayoutHorizontalDisplayOptionsMax) {
- int spaceBetweenItems = availableWidth
- - Math.round(widthPerItem * mLinearLayoutHorizontalDisplayOptionsMax)
- - mContainer.getPaddingLeft();
- int itemEndMargin =
- spaceBetweenItems / (int) mLinearLayoutHorizontalDisplayOptionsMax;
- itemEndMargin = Math.max(itemEndMargin, res.getDimensionPixelOffset(
- R.dimen.option_tile_margin_horizontal));
- mContainer.addItemDecoration(new ItemEndHorizontalSpaceItemDecoration(
- mContainer.getContext(), itemEndMargin));
- return;
- }
-
- int spaceBetweenItems = extraSpace / (mAdapter.getItemCount() + 1);
- int itemSideMargin = spaceBetweenItems / 2;
- mContainer.addItemDecoration(new HorizontalSpacerItemDecoration(itemSideMargin));
- }
- }
-
- public void resetOptions(List<T> options) {
- mOptions.clear();
- mOptions.addAll(options);
- mAdapter.notifyDataSetChanged();
- }
-
- private void notifyListeners() {
- if (mListeners.isEmpty()) {
- return;
- }
- T option = mSelectedOption;
- Set<OptionSelectedListener> iterableListeners = new HashSet<>(mListeners);
- for (OptionSelectedListener listener : iterableListeners) {
- listener.onOptionSelected(option);
- }
- }
-
- private static class TileViewHolder extends RecyclerView.ViewHolder {
- TextView labelView;
- View tileView;
- CharSequence title;
-
- TileViewHolder(@NonNull View itemView) {
- super(itemView);
- labelView = itemView.findViewById(R.id.option_label);
- tileView = itemView.findViewById(R.id.option_tile);
- title = null;
- }
-
- /**
- * Set the content description for this holder using the given string id.
- * If the option does not have a label, the description will be set on the tile view.
- *
- * @param context The view's context
- * @param option The customization option
- * @param id Resource ID of the string to use for the content description
- */
- public void setContentDescription(Context context, CustomizationOption<?> option, int id) {
- title = option.getTitle();
- if (TextUtils.isEmpty(title) && tileView != null) {
- title = tileView.getContentDescription();
- }
-
- CharSequence cd = context.getString(id, title);
- if (labelView != null && !TextUtils.isEmpty(labelView.getText())) {
- labelView.setAccessibilityPaneTitle(cd);
- labelView.setContentDescription(cd);
- } else if (tileView != null) {
- tileView.setAccessibilityPaneTitle(cd);
- tileView.setContentDescription(cd);
- }
- }
-
- public void resetContentDescription() {
- if (labelView != null && !TextUtils.isEmpty(labelView.getText())) {
- labelView.setAccessibilityPaneTitle(title);
- labelView.setContentDescription(title);
- } else if (tileView != null) {
- tileView.setAccessibilityPaneTitle(title);
- tileView.setContentDescription(title);
- }
- }
- }
-
- private class OptionSelectorAccessibilityDelegate extends RecyclerViewAccessibilityDelegate {
-
- OptionSelectorAccessibilityDelegate(RecyclerView recyclerView) {
- super(recyclerView);
- }
-
- @Override
- public boolean onRequestSendAccessibilityEvent(
- ViewGroup host, View child, AccessibilityEvent event) {
-
- // Apply this workaround to horizontal recyclerview only,
- // since the symptom is TalkBack will lose focus when navigating horizontal list items.
- if (mContainer.getLayoutManager() != null
- && mContainer.getLayoutManager().canScrollHorizontally()
- && event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
- int itemPos = mContainer.getChildLayoutPosition(child);
- int itemWidth = mContainer.getContext().getResources()
- .getDimensionPixelOffset(R.dimen.option_tile_width);
- int itemMarginHorizontal = mContainer.getContext().getResources()
- .getDimensionPixelOffset(R.dimen.option_tile_margin_horizontal) * 2;
- int scrollOffset = itemWidth + itemMarginHorizontal;
-
- // Make focusing item's previous/next item totally visible when changing focus,
- // ensure TalkBack won't lose focus when recyclerview scrolling.
- if (itemPos >= ((LinearLayoutManager) mContainer.getLayoutManager())
- .findLastCompletelyVisibleItemPosition()) {
- mContainer.scrollBy(scrollOffset, 0);
- } else if (itemPos <= ((LinearLayoutManager) mContainer.getLayoutManager())
- .findFirstCompletelyVisibleItemPosition() && itemPos != 0) {
- mContainer.scrollBy(-scrollOffset, 0);
- }
- }
- return super.onRequestSendAccessibilityEvent(host, child, event);
- }
- }
-
- /** Custom ItemDecorator to add specific spacing between items in the list. */
- private static final class ItemEndHorizontalSpaceItemDecoration
- extends RecyclerView.ItemDecoration {
- private final int mHorizontalSpacePx;
- private final boolean mDirectionLTR;
-
- private ItemEndHorizontalSpaceItemDecoration(Context context, int horizontalSpacePx) {
- mDirectionLTR = context.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR;
- mHorizontalSpacePx = horizontalSpacePx;
- }
-
- @Override
- public void getItemOffsets(Rect outRect, View view, RecyclerView recyclerView,
- RecyclerView.State state) {
- if (recyclerView.getAdapter() == null) {
- return;
- }
-
- if (recyclerView.getChildAdapterPosition(view)
- != checkNotNull(recyclerView.getAdapter()).getItemCount() - 1) {
- // Don't add spacing behind the last item
- if (mDirectionLTR) {
- outRect.right = mHorizontalSpacePx;
- } else {
- outRect.left = mHorizontalSpacePx;
- }
- }
- }
- }
-}