material-components / material-components-android

Modular and customizable Material Design UI components for Android
Apache License 2.0
16.27k stars 3.06k forks source link

[MaterialSwitch] thumb icon drawable is not properly tinted in light themes with certain characteristics #3535

Open bubbleguuum opened 1 year ago

bubbleguuum commented 1 year ago

Let's a consider a MaterialSwitch with a check mark thumb icon drawable specified with app:thumIcon="@drawable/material_switch_check_selector":

material_switch_check_selector.xml

<selector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize="true">
    <item
        android:state_checked="true"
        android:drawable="@drawable/material_switch_check" />
</selector>

material_switch_check.xml

<vector android:height="16dp"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

This usually works fine and the Switch will show the check drawable when it is checked.

However, if a light theme has a dark color for colorPrimaryContainer and a light color for colorOnPrimaryContainer, the check drawable tinting does not work properly, ending up in the check mark drawable being invisible.

This situation does not seem to happen with dynamic Material You themes but can happen if you generate a theme with the color library. For example a theme generated with SchemeContent or SchemeExpressive with seed color #5c76aa (a darker blue) produces a dark colorPrimaryContainer and a light (white actually) colorOnPrimaryContainer, resulting in the Switch check mark not being visible when the switch is checked. SchemeMonochrome results in a check mark barely visible (white on light gray background). All other schemes are unaffected as they produce a light colorPrimaryContainer and dark colorOnPrimaryContainer.

Here's the code that generate both colors. I wonder if for SchemeContent or SchemeExpressive it should not generate (for a light theme) a light colorPrimaryContainer and dark colorOnPrimaryContainer like for other schemes ?:

@NonNull
  public DynamicColor primaryContainer() {
    return new DynamicColor(
        /* name= */ "primary_container",
        /* palette= */ (s) -> s.primaryPalette,
        /* tone= */ (s) -> {
          if (isFidelity(s)) {
            return performAlbers(s.sourceColorHct, s);
          }
          if (isMonochrome(s)) {
            return s.isDark ? 85.0 : 25.0;
          }
          return s.isDark ? 30.0 : 90.0;
        },
        /* isBackground= */ true,
        /* background= */ this::highestSurface,
        /* secondBackground= */ null,
        /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 7.0),
        /* toneDeltaPair= */ (s) ->
            new ToneDeltaPair(primaryContainer(), primary(), 15.0, TonePolarity.NEARER, false));
  }

  @NonNull
  public DynamicColor onPrimaryContainer() {
    return new DynamicColor(
        /* name= */ "on_primary_container",
        /* palette= */ (s) -> s.primaryPalette,
        /* tone= */ (s) -> {
          if (isFidelity(s)) {
            return DynamicColor.foregroundTone(primaryContainer().tone.apply(s), 4.5);
          }
          if (isMonochrome(s)) {
            return s.isDark ? 0.0 : 100.0;
          }
          return s.isDark ? 90.0 : 10.0;
        },
        /* isBackground= */ false,
        /* background= */ (s) -> primaryContainer(),
        /* secondBackground= */ null,
        /* contrastCurve= */ new ContrastCurve(4.5, 7.0, 11.0, 21.0),
        /* toneDeltaPair= */ null);
  }

The easiest way to reproduce this issue is to have a MaterialSwitch with app:thumIcon="@drawable/material_switch_check_selector"and a light theme with:

 <item name="colorPrimaryContainer">#5a74a8</item>
 <item name="colorOnPrimaryContainer">#ffffff</item>

Which are the colors generated by SchemeContent for seed color #5c76aa.

More broadly, does M3 requires that in a light theme , colorPrimaryContainer must always be a light color and colorOnPrimaryContainer a dark one ? If that's the case, the colors returned by SchemeContent and SchemeFidelity are not always compliant.

bubbleguuum commented 1 year ago

Forgot to mentioned this was observed on version 1.11.0-alpha02.

Also, the animation from unchecked to checked is wrong, as the check mark drawable is briefly visible during the animation before becoming entirely invisible in checked state.