blob: 791a9a0a081474c276c2f1b1278ec724ffb0c011 [file] [log] [blame]
/*
* 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 static com.android.customization.model.color.ColorOptionsProvider.COLOR_SOURCE_HOME;
import static com.android.customization.model.color.ColorOptionsProvider.COLOR_SOURCE_LOCK;
import static com.android.customization.model.color.ColorOptionsProvider.COLOR_SOURCE_PRESET;
import android.app.Activity;
import android.app.WallpaperColors;
import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
import android.stats.style.StyleEnums;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import com.android.customization.model.CustomizationManager;
import com.android.customization.model.theme.OverlayManagerCompat;
import com.android.customization.module.CustomizationInjector;
import com.android.customization.module.ThemesUserEventLogger;
import com.android.customization.picker.color.ui.fragment.ColorPickerFragment;
import com.android.customization.picker.color.ui.view.ColorSectionView2;
import com.android.wallpaper.R;
import com.android.wallpaper.model.CustomizationSectionController;
import com.android.wallpaper.model.WallpaperColorsViewModel;
import com.android.wallpaper.module.InjectorProvider;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
/**
* Color section view's controller for the logic of color customization.
*
* TODO (b/262924584): Convert ColorSectionController2 into Kotlin & use new architecture
*/
public class ColorSectionController2 implements CustomizationSectionController<ColorSectionView2> {
private static final String TAG = "ColorSectionController";
private static final long MIN_COLOR_APPLY_PERIOD = 500L;
private final ThemesUserEventLogger mEventLogger;
private final ColorCustomizationManager mColorManager;
private final WallpaperColorsViewModel mWallpaperColorsViewModel;
private final LifecycleOwner mLifecycleOwner;
private final CustomizationSectionNavigationController mSectionNavigationController;
private List<ColorOption> mWallpaperColorOptions = new ArrayList<>();
private List<ColorOption> mPresetColorOptions = new ArrayList<>();
private ColorOption mSelectedColor;
@Nullable private WallpaperColors mHomeWallpaperColors;
@Nullable private WallpaperColors mLockWallpaperColors;
// Uses a boolean value to indicate whether wallpaper color is ready because WallpaperColors
// maybe be null when it's ready.
private boolean mHomeWallpaperColorsReady;
private boolean mLockWallpaperColorsReady;
private long mLastColorApplyingTime = 0L;
private ColorSectionView2 mColorSectionView;
public ColorSectionController2(Activity activity, WallpaperColorsViewModel viewModel,
LifecycleOwner lifecycleOwner,
CustomizationSectionNavigationController sectionNavigationController) {
CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
mEventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(activity);
mColorManager = ColorCustomizationManager.getInstance(activity,
new OverlayManagerCompat(activity));
mWallpaperColorsViewModel = viewModel;
mLifecycleOwner = lifecycleOwner;
mSectionNavigationController = sectionNavigationController;
}
@Override
public boolean isAvailable(@Nullable Context context) {
return context != null && ColorUtils.isMonetEnabled(context) && mColorManager.isAvailable();
}
@Override
public ColorSectionView2 createView(Context context) {
mColorSectionView = (ColorSectionView2) LayoutInflater.from(context).inflate(
R.layout.color_section_view2, /* root= */ null);
mWallpaperColorsViewModel.getHomeWallpaperColorsLiveData().observe(mLifecycleOwner,
homeColors -> {
mHomeWallpaperColors = homeColors;
mHomeWallpaperColorsReady = true;
maybeLoadColors();
});
mWallpaperColorsViewModel.getLockWallpaperColorsLiveData().observe(mLifecycleOwner,
lockColors -> {
mLockWallpaperColors = lockColors;
mLockWallpaperColorsReady = true;
maybeLoadColors();
});
return mColorSectionView;
}
private void maybeLoadColors() {
if (mHomeWallpaperColorsReady && mLockWallpaperColorsReady) {
mColorManager.setWallpaperColors(mHomeWallpaperColors, mLockWallpaperColors);
loadColorOptions(/* reload= */ false);
}
}
private void loadColorOptions(boolean reload) {
mColorManager.fetchOptions(new CustomizationManager.OptionsFetchedListener<ColorOption>() {
@Override
public void onOptionsLoaded(List<ColorOption> options) {
List<ColorOption> wallpaperColorOptions = new ArrayList<>();
List<ColorOption> presetColorOptions = new ArrayList<>();
for (ColorOption option : options) {
if (option instanceof ColorSeedOption) {
wallpaperColorOptions.add(option);
} else if (option instanceof ColorBundle) {
presetColorOptions.add(option);
}
}
mWallpaperColorOptions = wallpaperColorOptions;
mPresetColorOptions = presetColorOptions;
mSelectedColor = findActiveColorOption(mWallpaperColorOptions,
mPresetColorOptions);
mColorSectionView.post(() -> setUpColorSectionView(mWallpaperColorOptions,
mPresetColorOptions));
}
@Override
public void onError(@Nullable Throwable throwable) {
if (throwable != null) {
Log.e(TAG, "Error loading theme bundles", throwable);
}
}
}, reload);
}
private void setUpColorSectionView(List<ColorOption> wallpaperColorOptions,
List<ColorOption> presetColorOptions) {
int wallpaperOptionSize = wallpaperColorOptions.size();
List<ColorOption> subOptions = wallpaperColorOptions.subList(0,
Math.min(5, wallpaperOptionSize));
// add additional options based on preset colors if there are less than 5 wallpaper colors
List<ColorOption> additionalSubOptions = presetColorOptions.subList(0,
Math.min(Math.max(0, 5 - wallpaperOptionSize), presetColorOptions.size()));
subOptions.addAll(additionalSubOptions);
mColorSectionView.setOverflowOnClick(() -> {
mSectionNavigationController.navigateTo(new ColorPickerFragment());
return null;
});
mColorSectionView.setColorOptionOnClick(selectedOption -> {
if (mSelectedColor.equals(selectedOption)) {
return null;
}
mSelectedColor = (ColorOption) selectedOption;
// Post with delay for color option to run ripple.
new Handler().postDelayed(()-> applyColor(mSelectedColor), /* delayMillis= */ 100);
return null;
});
mColorSectionView.setItems(subOptions, mColorManager);
}
private ColorOption findActiveColorOption(List<ColorOption> wallpaperColorOptions,
List<ColorOption> presetColorOptions) {
ColorOption activeColorOption = null;
for (ColorOption colorOption : Lists.newArrayList(
Iterables.concat(wallpaperColorOptions, presetColorOptions))) {
if (colorOption.isActive(mColorManager)) {
activeColorOption = colorOption;
break;
}
}
// Use the first one option by default. This should not happen as above should have an
// active option found.
if (activeColorOption == null) {
activeColorOption = wallpaperColorOptions.isEmpty()
? presetColorOptions.get(0)
: wallpaperColorOptions.get(0);
}
return activeColorOption;
}
private void applyColor(ColorOption colorOption) {
if (SystemClock.elapsedRealtime() - mLastColorApplyingTime < MIN_COLOR_APPLY_PERIOD) {
return;
}
mLastColorApplyingTime = SystemClock.elapsedRealtime();
mColorManager.apply(colorOption, new CustomizationManager.Callback() {
@Override
public void onSuccess() {
mColorSectionView.announceForAccessibility(
mColorSectionView.getContext().getString(R.string.color_changed));
mEventLogger.logColorApplied(getColorAction(colorOption), colorOption);
}
@Override
public void onError(@Nullable Throwable throwable) {
Log.w(TAG, "Apply theme with error: " + throwable);
}
});
}
private int getColorAction(ColorOption colorOption) {
int action = StyleEnums.DEFAULT_ACTION;
boolean isForBoth = mLockWallpaperColors == null || mLockWallpaperColors.equals(
mHomeWallpaperColors);
if (TextUtils.equals(colorOption.getSource(), COLOR_SOURCE_PRESET)) {
action = StyleEnums.COLOR_PRESET_APPLIED;
} else if (isForBoth) {
action = StyleEnums.COLOR_WALLPAPER_HOME_LOCK_APPLIED;
} else {
switch (colorOption.getSource()) {
case COLOR_SOURCE_HOME:
action = StyleEnums.COLOR_WALLPAPER_HOME_APPLIED;
break;
case COLOR_SOURCE_LOCK:
action = StyleEnums.COLOR_WALLPAPER_LOCK_APPLIED;
break;
}
}
return action;
}
}