From 1dbd3dd2e988462711718010dd80241b0779739b Mon Sep 17 00:00:00 2001 From: Material Design Team Date: Tue, 3 May 2022 11:27:45 -0400 Subject: [PATCH] [M3][Color] Updated color harmonization demo PiperOrigin-RevId: 446199668 (cherry picked from commit dc65df3acebbc1eed10cec2e4f46282225e56354) --- .../ColorHarmonizationDemoFragmentTest.java | 37 ++- .../io/material/catalog/color/ColorGrid.java | 2 +- .../color/ColorHarmonizationDemoFragment.java | 167 ++++++---- .../color/ColorHarmonizationGridRowData.java | 61 ++++ .../io/material/catalog/color/ColorRow.java | 15 +- .../catalog/color/HarmonizableButton.java | 56 ++++ .../catalog/color/HarmonizableButtonData.java | 48 +++ .../cat_colors_harmonization_fragment.xml | 293 ++++++++++++++---- .../catalog/color/res/values/colors.xml | 9 +- .../catalog/color/res/values/strings.xml | 56 ++++ 10 files changed, 598 insertions(+), 146 deletions(-) create mode 100644 catalog/java/io/material/catalog/color/ColorHarmonizationGridRowData.java create mode 100644 catalog/java/io/material/catalog/color/HarmonizableButton.java create mode 100644 catalog/java/io/material/catalog/color/HarmonizableButtonData.java diff --git a/catalog/androidTest/javatests/io/material/catalog/color/ColorHarmonizationDemoFragmentTest.java b/catalog/androidTest/javatests/io/material/catalog/color/ColorHarmonizationDemoFragmentTest.java index c2605777e0d..754882f4342 100644 --- a/catalog/androidTest/javatests/io/material/catalog/color/ColorHarmonizationDemoFragmentTest.java +++ b/catalog/androidTest/javatests/io/material/catalog/color/ColorHarmonizationDemoFragmentTest.java @@ -17,7 +17,7 @@ package io.material.catalog.color; import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.scrollTo; import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withId; @@ -32,7 +32,7 @@ import org.junit.Test; import org.junit.runner.RunWith; -/** Tests for {@link ColorHarmonizationFragment} */ +/** Tests for {@link ColorHarmonizationDemoFragment} */ @MediumTest @RunWith(AndroidJUnit4.class) public class ColorHarmonizationDemoFragmentTest { @@ -57,11 +57,34 @@ public void setUpAndLaunchFragment() { } @Test - public void checkColorHexValueTextIsShown() { - onView(withId(R.id.cat_color_enabled_switch)).perform(click()); + public void checkButtonsAreShown() { + onView(withId(R.id.red_button_dark)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.red_button_light)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.yellow_button_dark)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.yellow_button_light)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.green_button_dark)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.green_button_light)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.blue_button_dark)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.blue_button_light)).perform(scrollTo()).check(matches(isDisplayed())); + } - onView(withId(R.id.material_button_color_hex_value)).check(matches(isDisplayed())); - onView(withId(R.id.material_unelevated_button_color_hex_value)).check(matches(isDisplayed())); - onView(withId(R.id.material_text_input_color_hex_value)).check(matches(isDisplayed())); + @Test + public void checkColorPalettesAreShown() { + onView(withId(R.id.cat_colors_error)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.cat_colors_harmonized_error)) + .perform(scrollTo()) + .check(matches(isDisplayed())); + onView(withId(R.id.cat_colors_yellow)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.cat_colors_harmonized_yellow)) + .perform(scrollTo()) + .check(matches(isDisplayed())); + onView(withId(R.id.cat_colors_green)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.cat_colors_harmonized_green)) + .perform(scrollTo()) + .check(matches(isDisplayed())); + onView(withId(R.id.cat_colors_blue)).perform(scrollTo()).check(matches(isDisplayed())); + onView(withId(R.id.cat_colors_harmonized_blue)) + .perform(scrollTo()) + .check(matches(isDisplayed())); } } diff --git a/catalog/java/io/material/catalog/color/ColorGrid.java b/catalog/java/io/material/catalog/color/ColorGrid.java index 5349251b68b..c3d3c9729ee 100644 --- a/catalog/java/io/material/catalog/color/ColorGrid.java +++ b/catalog/java/io/material/catalog/color/ColorGrid.java @@ -35,7 +35,7 @@ final class ColorGrid { @NonNull private final MaterialColorSpec materialColorSpecAccentContainer; @NonNull private final MaterialColorSpec materialColorSpecOnAccentContainer; - static ColorGrid createFromColorGridData(Context context, ColorGridData colorGridData) { + static ColorGrid createFromColorGridData(ColorGridData colorGridData) { ColorRoles colorRoles = colorGridData.getColorRoles(); ColorRoleNames colorRoleNames = colorGridData.getColorRoleNames(); MaterialColorSpec[] materialColorSpecs = diff --git a/catalog/java/io/material/catalog/color/ColorHarmonizationDemoFragment.java b/catalog/java/io/material/catalog/color/ColorHarmonizationDemoFragment.java index 073f37cff7f..f84c3a0696e 100644 --- a/catalog/java/io/material/catalog/color/ColorHarmonizationDemoFragment.java +++ b/catalog/java/io/material/catalog/color/ColorHarmonizationDemoFragment.java @@ -18,28 +18,75 @@ import io.material.catalog.R; +import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.LayoutRes; +import android.widget.LinearLayout; +import androidx.annotation.IdRes; import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import com.google.android.material.button.MaterialButton; -import com.google.android.material.color.MaterialColors; +import com.google.android.material.color.DynamicColors; +import com.google.android.material.color.HarmonizedColors; +import com.google.android.material.color.HarmonizedColorsOptions; import com.google.android.material.switchmaterial.SwitchMaterial; -import com.google.android.material.textfield.TextInputLayout; import io.material.catalog.feature.DemoFragment; +import java.util.ArrayList; +import java.util.List; /** A fragment that displays the Color Harmonization demo for the Catalog app. */ public class ColorHarmonizationDemoFragment extends DemoFragment { - private static final int GREEN_RESOURCE_ID = R.color.green40; - private static final int RED_RESOURCE_ID = R.color.red40; - private static final int BLUE_RESOURCE_ID = R.color.blue90; + private static final HarmonizableButtonData[] HARMONIZABLE_BUTTON_DATA_LIST = + new HarmonizableButtonData[] { + new HarmonizableButtonData( + R.id.red_button_dark, R.color.error_reference, /* isLightButton= */ false), + new HarmonizableButtonData( + R.id.red_button_light, R.color.error_reference, /* isLightButton= */ true), + new HarmonizableButtonData( + R.id.yellow_button_dark, R.color.yellow_reference, /* isLightButton= */ false), + new HarmonizableButtonData( + R.id.yellow_button_light, R.color.yellow_reference, /* isLightButton= */ true), + new HarmonizableButtonData( + R.id.green_button_dark, R.color.green_reference, /* isLightButton= */ false), + new HarmonizableButtonData( + R.id.green_button_light, R.color.green_reference, /* isLightButton= */ true), + new HarmonizableButtonData( + R.id.blue_button_dark, R.color.blue_reference, /* isLightButton= */ false), + new HarmonizableButtonData( + R.id.blue_button_light, R.color.blue_reference, /* isLightButton= */ true), + }; + // TODO(b/231143697): Refactor this class to a DemoActivity and showcase harmonization using + // error color attributes. + private static final ColorHarmonizationGridRowData[] HARMONIZATION_GRID_ROW_DATA_LIST = + new ColorHarmonizationGridRowData[] { + new ColorHarmonizationGridRowData( + R.id.cat_colors_error, + R.id.cat_colors_harmonized_error, + R.color.error_reference, + R.array.cat_error_strings), + new ColorHarmonizationGridRowData( + R.id.cat_colors_yellow, + R.id.cat_colors_harmonized_yellow, + R.color.yellow_reference, + R.array.cat_yellow_strings), + new ColorHarmonizationGridRowData( + R.id.cat_colors_green, + R.id.cat_colors_harmonized_green, + R.color.green_reference, + R.array.cat_green_strings), + new ColorHarmonizationGridRowData( + R.id.cat_colors_blue, + R.id.cat_colors_harmonized_blue, + R.color.blue_reference, + R.array.cat_blue_strings) + }; + + private Context dynamicColorsContext; + private Context harmonizedContext; + private View demoView; + + private final List harmonizableButtonList = new ArrayList<>(); @Nullable @Override @@ -47,59 +94,61 @@ public View onCreateDemoView( @Nullable LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) { - View view = layoutInflater.inflate(getColorsContent(), viewGroup, false /* attachToRoot */); - MaterialButton elevatedButton = view.findViewById(R.id.material_button); - MaterialButton unelevatedButton = view.findViewById(R.id.material_unelevated_button); - TextInputLayout textInputLayout = view.findViewById(R.id.material_text_input_layout); - TextView buttonColorHexValueText = view.findViewById(R.id.material_button_color_hex_value); - TextView unelevatedButtonColorHexValueText = - view.findViewById(R.id.material_unelevated_button_color_hex_value); - TextView textInputColorHexValueText = - view.findViewById(R.id.material_text_input_color_hex_value); - - elevatedButton.setBackgroundColor(getResources().getColor(GREEN_RESOURCE_ID)); - unelevatedButton.setBackgroundColor(getResources().getColor(RED_RESOURCE_ID)); - textInputLayout.setBoxBackgroundColor(getResources().getColor(BLUE_RESOURCE_ID)); - - SwitchMaterial enabledSwitch = view.findViewById(R.id.cat_color_enabled_switch); - enabledSwitch.setOnCheckedChangeListener( - (buttonView, isChecked) -> { - buttonColorHexValueText.setVisibility(View.VISIBLE); - unelevatedButtonColorHexValueText.setVisibility(View.VISIBLE); - textInputColorHexValueText.setVisibility(View.VISIBLE); + demoView = + layoutInflater.inflate( + R.layout.cat_colors_harmonization_fragment, viewGroup, false /* attachToRoot */); - int maybeHarmonizedGreen = maybeHarmonizeWithPrimary(GREEN_RESOURCE_ID, isChecked); - int maybeHarmonizedRed = maybeHarmonizeWithPrimary(RED_RESOURCE_ID, isChecked); - int maybeHarmonizedBlue = maybeHarmonizeWithPrimary(BLUE_RESOURCE_ID, isChecked); - - elevatedButton.setBackgroundColor(maybeHarmonizedGreen); - unelevatedButton.setBackgroundColor(maybeHarmonizedRed); - textInputLayout.setBoxBackgroundColor(maybeHarmonizedBlue); - - // The %06X gives us zero-padded hex (always 6 chars long). - buttonColorHexValueText.setText( - getColorHexValueText(R.string.cat_color_hex_value_text, maybeHarmonizedGreen)); - unelevatedButtonColorHexValueText.setText( - getColorHexValueText(R.string.cat_color_hex_value_text, maybeHarmonizedRed)); - textInputColorHexValueText.setText( - getColorHexValueText(R.string.cat_color_hex_value_text, maybeHarmonizedBlue)); - }); - - return view; - } + dynamicColorsContext = DynamicColors.wrapContextIfAvailable(requireContext()); + HarmonizedColorsOptions options = + new HarmonizedColorsOptions.Builder() + .setColorResourceIds( + new int[] { + R.color.error_reference, + R.color.yellow_reference, + R.color.blue_reference, + R.color.green_reference, + }) + .build(); + harmonizedContext = HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, options); - private int maybeHarmonizeWithPrimary(@ColorRes int colorResId, boolean harmonize) { - return harmonize - ? MaterialColors.harmonizeWithPrimary(getContext(), getResources().getColor(colorResId)) - : getResources().getColor(colorResId); + for (ColorHarmonizationGridRowData colorHarmonizationGridRowData : + HARMONIZATION_GRID_ROW_DATA_LIST) { + createColorGridAndPopulateLayout( + dynamicColorsContext, + colorHarmonizationGridRowData, + colorHarmonizationGridRowData.getLeftLayoutId()); + createColorGridAndPopulateLayout( + harmonizedContext, + colorHarmonizationGridRowData, + colorHarmonizationGridRowData.getRightLayoutId()); + } + // Setup buttons text color based on current theme. + for (HarmonizableButtonData harmonizableButtonData : HARMONIZABLE_BUTTON_DATA_LIST) { + harmonizableButtonList.add(HarmonizableButton.create(demoView, harmonizableButtonData)); + } + updateButtons(/* harmonize= */ false); + SwitchMaterial enabledSwitch = demoView.findViewById(R.id.cat_color_enabled_switch); + enabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> updateButtons(isChecked)); + return demoView; } - private CharSequence getColorHexValueText(@StringRes int stringResId, @ColorInt int color) { - return getResources().getString(stringResId, String.format("#%06X", (0xFFFFFF & color))); + private void createColorGridAndPopulateLayout( + Context context, + ColorHarmonizationGridRowData colorHarmonizationGridRowData, + @IdRes int layoutId) { + ColorGrid colorGrid = + ColorGrid.createFromColorGridData( + ColorGridData.createFromColorResId( + context, + colorHarmonizationGridRowData.getColorResId(), + colorHarmonizationGridRowData.getColorNameIds())); + LinearLayout layout = demoView.findViewById(layoutId); + layout.addView(colorGrid.renderView(context, layout)); } - @LayoutRes - protected int getColorsContent() { - return R.layout.cat_colors_harmonization_fragment; + private void updateButtons(boolean harmonize) { + for (HarmonizableButton button : harmonizableButtonList) { + button.updateColors(harmonize); + } } } diff --git a/catalog/java/io/material/catalog/color/ColorHarmonizationGridRowData.java b/catalog/java/io/material/catalog/color/ColorHarmonizationGridRowData.java new file mode 100644 index 00000000000..7ba216e50f0 --- /dev/null +++ b/catalog/java/io/material/catalog/color/ColorHarmonizationGridRowData.java @@ -0,0 +1,61 @@ +/* + * Copyright 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 io.material.catalog.color; + +import androidx.annotation.ArrayRes; +import androidx.annotation.ColorRes; +import androidx.annotation.IdRes; + +/** A class that provides data for a row in the Color Harmonization demo grid. */ +final class ColorHarmonizationGridRowData { + + @IdRes private final int leftLayoutId; + @IdRes private final int rightLayoutId; + @ColorRes private final int colorResId; + @ArrayRes private final int colorNameIds; + + ColorHarmonizationGridRowData( + @IdRes int leftLayoutId, + @IdRes int rightLayoutId, + @ColorRes int colorResId, + @ArrayRes int colorNameIds) { + this.leftLayoutId = leftLayoutId; + this.rightLayoutId = rightLayoutId; + this.colorResId = colorResId; + this.colorNameIds = colorNameIds; + } + + @IdRes + int getLeftLayoutId() { + return leftLayoutId; + } + + @IdRes + int getRightLayoutId() { + return rightLayoutId; + } + + @ColorRes + int getColorResId() { + return colorResId; + } + + @ArrayRes + int getColorNameIds() { + return colorNameIds; + } +} diff --git a/catalog/java/io/material/catalog/color/ColorRow.java b/catalog/java/io/material/catalog/color/ColorRow.java index 1cbd20df13c..6717f490b0d 100644 --- a/catalog/java/io/material/catalog/color/ColorRow.java +++ b/catalog/java/io/material/catalog/color/ColorRow.java @@ -18,7 +18,6 @@ import io.material.catalog.R; -import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; @@ -75,16 +74,8 @@ private void bindColorRoleItem( TextView colorRole = view.findViewById(textViewId); colorRole.setText(colorRoleTextResID); - colorRole.setTextColor(getTextColor(colorAttrResId)); - colorRole.setBackgroundColor(MaterialColors.getColor(view, colorAttrResId)); - } - - private int getTextColor(@AttrRes int colorAttrResId) { - if (!MaterialColors.isColorLight(MaterialColors.getColor(catColorsSchemeRow, colorAttrResId))) { - // Use white text color if the color is considered dark. - return Color.WHITE; - } else { - return Color.BLACK; - } + colorRole.setTextColor( + ColorDemoUtils.getTextColor(MaterialColors.getColor(catColorsSchemeRow, colorAttrResId))); + colorRole.setBackgroundColor(MaterialColors.getColor(catColorsSchemeRow, colorAttrResId)); } } diff --git a/catalog/java/io/material/catalog/color/HarmonizableButton.java b/catalog/java/io/material/catalog/color/HarmonizableButton.java new file mode 100644 index 00000000000..83073d85d8d --- /dev/null +++ b/catalog/java/io/material/catalog/color/HarmonizableButton.java @@ -0,0 +1,56 @@ +/* + * Copyright 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 io.material.catalog.color; + +import android.view.View; +import androidx.annotation.ColorInt; +import com.google.android.material.button.MaterialButton; +import com.google.android.material.color.ColorRoles; +import com.google.android.material.color.MaterialColors; + +/** A class for {@link MaterialButton} that can be harmonized. */ +final class HarmonizableButton { + + private final MaterialButton materialButton; + private final int colorValue; + + static HarmonizableButton create(View view, HarmonizableButtonData harmonizableButtonData) { + ColorRoles colorRoles = + MaterialColors.getColorRoles( + view.getContext(), + view.getResources().getColor(harmonizableButtonData.getColorResId())); + return new HarmonizableButton( + view.findViewById(harmonizableButtonData.getButtonId()), + harmonizableButtonData.isLightButton() + ? colorRoles.getAccentContainer() + : colorRoles.getAccent()); + } + + private HarmonizableButton(MaterialButton materialButton, @ColorInt int colorValue) { + this.materialButton = materialButton; + this.colorValue = colorValue; + } + + void updateColors(boolean harmonize) { + int maybeHarmonizedColor = + harmonize + ? MaterialColors.harmonizeWithPrimary(materialButton.getContext(), colorValue) + : colorValue; + materialButton.setBackgroundColor(maybeHarmonizedColor); + materialButton.setTextColor(ColorDemoUtils.getTextColor(maybeHarmonizedColor)); + } +} diff --git a/catalog/java/io/material/catalog/color/HarmonizableButtonData.java b/catalog/java/io/material/catalog/color/HarmonizableButtonData.java new file mode 100644 index 00000000000..6fdf4cc2e88 --- /dev/null +++ b/catalog/java/io/material/catalog/color/HarmonizableButtonData.java @@ -0,0 +1,48 @@ +/* + * Copyright 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 io.material.catalog.color; + +import androidx.annotation.ColorRes; +import androidx.annotation.IdRes; + +/** A class that provides data for {@link HarmonizableButton}. */ +final class HarmonizableButtonData { + + @IdRes private final int buttonId; + @ColorRes private final int colorResId; + private final boolean isLightButton; + + HarmonizableButtonData(@IdRes int buttonId, @ColorRes int colorResId, boolean isLightButton) { + this.buttonId = buttonId; + this.colorResId = colorResId; + this.isLightButton = isLightButton; + } + + @IdRes + int getButtonId() { + return buttonId; + } + + @ColorRes + int getColorResId() { + return colorResId; + } + + boolean isLightButton() { + return isLightButton; + } +} diff --git a/catalog/java/io/material/catalog/color/res/layout/cat_colors_harmonization_fragment.xml b/catalog/java/io/material/catalog/color/res/layout/cat_colors_harmonization_fragment.xml index 3b87686c3f2..c4f3967533f 100644 --- a/catalog/java/io/material/catalog/color/res/layout/cat_colors_harmonization_fragment.xml +++ b/catalog/java/io/material/catalog/color/res/layout/cat_colors_harmonization_fragment.xml @@ -17,7 +17,6 @@ @@ -30,88 +29,258 @@ android:gravity="center_horizontal" android:orientation="vertical"> - + android:orientation="horizontal" + android:baselineAligned="false"> -