blob: a5ce222103479156b6ca65875bcb00dfe51136b0 [file] [log] [blame]
/*
* 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 org.json.JSONException;
import java.util.ArrayList;
import java.util.List;
public class CustomThemeActivity extends FragmentActivity implements
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 String CREATE_NEW_THEME = "CustomThemeActivity.NewTheme";
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;
private boolean mIsDefinedTheme = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
mUserEventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(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)) {
ThemeBundleProvider themeProvider =
new DefaultThemeProvider(this, injector.getCustomizationPreferences(this));
mIsDefinedTheme = intent.getBooleanExtra(CREATE_NEW_THEME, true);
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);
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);
}
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;
}
/**
* 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;
final ThemeComponentOptionProvider<T> provider;
final int position;
private CustomThemeStepFragment mFragment;
protected ComponentStep(@StringRes int titleResId, ThemeComponentOptionProvider<T> provider,
int position) {
this.titleResId = titleResId;
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, provider, position);
}
@Override
CustomThemeComponentFragment createFragment(String title) {
return CustomThemeComponentFragment.newInstance(
title,
position,
titleResId);
}
}
private class IconStep extends ComponentStep<IconOption> {
protected IconStep(ThemeComponentOptionProvider<IconOption> provider,
int position) {
super(R.string.icon_component_title, provider, position);
}
@Override
CustomThemeComponentFragment createFragment(String title) {
return CustomThemeComponentFragment.newInstance(
title,
position,
titleResId);
}
}
private class ColorStep extends ComponentStep<ColorOption> {
protected ColorStep(ThemeComponentOptionProvider<ColorOption> provider,
int position) {
super(R.string.color_component_title, provider, position);
}
@Override
CustomThemeComponentFragment createFragment(String title) {
return CustomThemeComponentFragment.newInstance(
title,
position,
titleResId,
true);
}
}
private class ShapeStep extends ComponentStep<ShapeOption> {
protected ShapeStep(ThemeComponentOptionProvider<ShapeOption> provider,
int position) {
super(R.string.shape_component_title, provider, position);
}
@Override
CustomThemeComponentFragment createFragment(String title) {
return CustomThemeComponentFragment.newInstance(
title,
position,
titleResId);
}
}
private class NameStep extends ComponentStep {
protected NameStep(int position) {
super(R.string.name_component_title, null, position);
}
@Override
CustomThemeNameFragment createFragment(String title) {
return CustomThemeNameFragment.newInstance(
title,
position,
titleResId,
mIsDefinedTheme);
}
}
}