Skip to content

Commit

Permalink
[M3][Color] Refactor DynamicColors overloading methods and added Dyna…
Browse files Browse the repository at this point in the history
…micColorsOptions class to support color harmonization.

PiperOrigin-RevId: 429324480
  • Loading branch information
Material Design Team authored and raajkumars committed Feb 18, 2022
1 parent a032884 commit bdbf052
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 22 deletions.
81 changes: 59 additions & 22 deletions lib/java/com/google/android/material/color/DynamicColors.java
Expand Up @@ -111,13 +111,6 @@ public boolean isSupported() {

private static final int USE_DEFAULT_THEME_OVERLAY = 0;

private static final Precondition ALWAYS_ALLOW = new Precondition() {
@Override
public boolean shouldApplyDynamicColors(@NonNull Activity activity, int theme) {
return true;
}
};

private DynamicColors() {}

/**
Expand All @@ -130,7 +123,7 @@ private DynamicColors() {}
* @param application The target application.
*/
public static void applyToActivitiesIfAvailable(@NonNull Application application) {
applyToActivitiesIfAvailable(application, USE_DEFAULT_THEME_OVERLAY);
applyIfAvailable(new DynamicColorsOptions.Builder(application).build());
}

/**
Expand All @@ -145,7 +138,7 @@ public static void applyToActivitiesIfAvailable(@NonNull Application application
*/
public static void applyToActivitiesIfAvailable(
@NonNull Application application, @StyleRes int theme) {
applyToActivitiesIfAvailable(application, theme, ALWAYS_ALLOW);
applyIfAvailable(new DynamicColorsOptions.Builder(application).setThemeOverlay(theme).build());
}

/**
Expand All @@ -160,7 +153,8 @@ public static void applyToActivitiesIfAvailable(
*/
public static void applyToActivitiesIfAvailable(
@NonNull Application application, @NonNull Precondition precondition) {
applyToActivitiesIfAvailable(application, USE_DEFAULT_THEME_OVERLAY, precondition);
applyIfAvailable(
new DynamicColorsOptions.Builder(application).setPrecondition(precondition).build());
}

/**
Expand Down Expand Up @@ -189,8 +183,11 @@ public static void applyToActivitiesIfAvailable(
*/
public static void applyToActivitiesIfAvailable(
@NonNull Application application, @StyleRes int theme, @NonNull Precondition precondition) {
application.registerActivityLifecycleCallbacks(
new DynamicColorsActivityLifecycleCallbacks(theme, precondition));
applyIfAvailable(
new DynamicColorsOptions.Builder(application)
.setThemeOverlay(theme)
.setPrecondition(precondition)
.build());
}

/**
Expand All @@ -200,7 +197,7 @@ public static void applyToActivitiesIfAvailable(
* @param activity The target activity.
*/
public static void applyIfAvailable(@NonNull Activity activity) {
applyIfAvailable(activity, USE_DEFAULT_THEME_OVERLAY);
applyIfAvailable(new DynamicColorsOptions.Builder(activity).build());
}

/**
Expand All @@ -210,7 +207,7 @@ public static void applyIfAvailable(@NonNull Activity activity) {
* @param theme The resource ID of the theme overlay that provides dynamic color definition.
*/
public static void applyIfAvailable(@NonNull Activity activity, @StyleRes int theme) {
applyIfAvailable(activity, theme, ALWAYS_ALLOW);
applyIfAvailable(new DynamicColorsOptions.Builder(activity).setThemeOverlay(theme).build());
}

/**
Expand All @@ -222,11 +219,41 @@ public static void applyIfAvailable(@NonNull Activity activity, @StyleRes int th
*/
public static void applyIfAvailable(
@NonNull Activity activity, @NonNull Precondition precondition) {
applyIfAvailable(activity, USE_DEFAULT_THEME_OVERLAY, precondition);
applyIfAvailable(
new DynamicColorsOptions.Builder(activity).setPrecondition(precondition).build());
}

/**
* Applies dynamic colors to the given application/activity with theme overlay according to the
* precondition specified in the dynamic colors options.
*
* @param dynamicColorsOptions The dynamic colors options object that specifies the theme resource
* ID, precondition to decide if dynamic colors should be applied and the callback function
* for after dynamic colors have been applied.
*/
public static void applyIfAvailable(@NonNull DynamicColorsOptions dynamicColorsOptions) {
Application application = dynamicColorsOptions.getApplication();
Activity activity = dynamicColorsOptions.getActivity();
if (application != null) {
application.registerActivityLifecycleCallbacks(
new DynamicColorsActivityLifecycleCallbacks(dynamicColorsOptions));
} else if (activity != null) {
applyIfAvailable(
activity,
dynamicColorsOptions.getThemeOverlay(),
dynamicColorsOptions.getPrecondition(),
dynamicColorsOptions.getOnAppliedCallback());
} else {
// This should not happen according to the design of DynamicColorsOptions.
throw new IllegalArgumentException("Either Application or Activity is required.");
}
}

private static void applyIfAvailable(
@NonNull Activity activity, @StyleRes int theme, @NonNull Precondition precondition) {
@NonNull Activity activity,
@StyleRes int theme,
@NonNull Precondition precondition,
@NonNull OnAppliedCallback onAppliedCallback) {
if (!isDynamicColorAvailable()) {
return;
}
Expand All @@ -235,6 +262,7 @@ private static void applyIfAvailable(
}
if (theme != 0 && precondition.shouldApplyDynamicColors(activity, theme)) {
applyDynamicColorThemeOverlay(activity, theme);
onAppliedCallback.onApplied(activity);
}
}

Expand Down Expand Up @@ -339,20 +367,29 @@ public interface Precondition {
boolean shouldApplyDynamicColors(@NonNull Activity activity, @StyleRes int theme);
}

/** The interface that provides a callback method after dynamic colors have been applied. */
public interface OnAppliedCallback {

/** The callback method after dynamic colors have been applied. */
void onApplied(@NonNull Activity activity);
}

private static class DynamicColorsActivityLifecycleCallbacks
implements ActivityLifecycleCallbacks {
private final int dynamicColorThemeOverlay;
private final Precondition precondition;
private final DynamicColorsOptions dynamicColorsOptions;

DynamicColorsActivityLifecycleCallbacks(@StyleRes int theme, @NonNull Precondition condition) {
dynamicColorThemeOverlay = theme;
precondition = condition;
DynamicColorsActivityLifecycleCallbacks(@NonNull DynamicColorsOptions options) {
this.dynamicColorsOptions = options;
}

@Override
public void onActivityPreCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
applyIfAvailable(activity, dynamicColorThemeOverlay, precondition);
applyIfAvailable(
activity,
dynamicColorsOptions.getThemeOverlay(),
dynamicColorsOptions.getPrecondition(),
dynamicColorsOptions.getOnAppliedCallback());
}

@Override
Expand Down
148 changes: 148 additions & 0 deletions lib/java/com/google/android/material/color/DynamicColorsOptions.java
@@ -0,0 +1,148 @@
/*
* 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.google.android.material.color;

import android.app.Activity;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import com.google.android.material.color.DynamicColors.OnAppliedCallback;
import com.google.android.material.color.DynamicColors.Precondition;

/** Wrapper class for specifying dynamic colors options when applying dynamic colors. */
public class DynamicColorsOptions {

private static final Precondition ALWAYS_ALLOW =
new Precondition() {
@Override
public boolean shouldApplyDynamicColors(@NonNull Activity activity, int theme) {
return true;
}
};

private static final OnAppliedCallback NO_OP_CALLBACK =
new OnAppliedCallback() {
@Override
public void onApplied(@NonNull Activity activity) {}
};

@Nullable private final Application application;
@Nullable private final Activity activity;
@StyleRes private final int themeOverlay;
@NonNull private final Precondition precondition;
@NonNull private final OnAppliedCallback onAppliedCallback;

private DynamicColorsOptions(Builder builder) {
this.application = builder.application;
this.activity = builder.activity;
this.themeOverlay = builder.themeOverlay;
this.precondition = builder.precondition;
this.onAppliedCallback = builder.onAppliedCallback;
}

/** Returns the application where dynamic color is applied. */
@Nullable
public Application getApplication() {
return application;
}

/** Returns the activity where dynamic color is applied. */
@Nullable
public Activity getActivity() {
return activity;
}

/** Returns the resource ID of the theme overlay that provides dynamic color definition. */
@StyleRes
public int getThemeOverlay() {
return themeOverlay;
}

/** Returns the precondition that decides if dynamic colors should be applied. */
@NonNull
public Precondition getPrecondition() {
return precondition;
}

/** Returns the callback method after dynamic colors have been applied. */
@NonNull
public OnAppliedCallback getOnAppliedCallback() {
return onAppliedCallback;
}

/**
* Builder class for specifying options when applying dynamic colors. When building {@code
* DynamicColorOptions}, either an {@link Application} or {@link Activity} is required. When
* {@link Application} is specified, dynamic colors will be applied to all activities in the
* application. For example:
* </pre>
* DynamicColorOptions dynamicColorOptions =
* new DynamicColorsOptions.Builder(application)
* .setThemeOverlay(themeOverlay)
* .setPrecondition(precondition)
* .setOnAppliedCallback(onAppliedCallback)
* .build()
* </pre>
*/
public static class Builder {

@Nullable private final Application application;
@Nullable private final Activity activity;
@StyleRes private int themeOverlay;
@NonNull private Precondition precondition = ALWAYS_ALLOW;
@NonNull private OnAppliedCallback onAppliedCallback = NO_OP_CALLBACK;

/** Sets the Application to apply dynamic colors on. */
public Builder(@NonNull Application application) {
this.application = application;
this.activity = null;
}

/** Sets the Activity to apply dynamic colors on. */
public Builder(@NonNull Activity activity) {
this.activity = activity;
this.application = null;
}

/** Sets the resource ID of the theme overlay that provides dynamic color definition. */
@NonNull
public Builder setThemeOverlay(@StyleRes int themeOverlay) {
this.themeOverlay = themeOverlay;
return this;
}

/** Sets the precondition that decides if dynamic colors should be applied. */
@NonNull
public Builder setPrecondition(@NonNull Precondition precondition) {
this.precondition = precondition;
return this;
}

/** Sets the callback method for after the dynamic colors have been applied. */
@NonNull
public Builder setOnAppliedCallback(@NonNull OnAppliedCallback onAppliedCallback) {
this.onAppliedCallback = onAppliedCallback;
return this;
}

@NonNull
public DynamicColorsOptions build() {
return new DynamicColorsOptions(this);
}
}
}

0 comments on commit bdbf052

Please sign in to comment.