Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7af55aae authored by Julia Reynolds's avatar Julia Reynolds Committed by Automerger Merge Worker
Browse files

Merge "Ensure text contrast" into udc-dev am: 0b02b77b

parents 69eb2d3d 0b02b77b
Loading
Loading
Loading
Loading
+57 −102
Original line number Diff line number Diff line
@@ -5286,7 +5286,7 @@ public class Notification implements Parcelable
            boolean hasSecondLine = showProgress;
            if (p.hasTitle()) {
                contentView.setViewVisibility(p.mTitleViewId, View.VISIBLE);
                contentView.setTextViewText(p.mTitleViewId, processTextSpans(p.mTitle));
                contentView.setTextViewText(p.mTitleViewId, ensureColorSpanContrast(p.mTitle, p));
                setTextViewColorPrimary(contentView, p.mTitleViewId, p);
            } else if (p.mTitleViewId != R.id.title) {
                // This alternate title view ID is not cleared by resetStandardTemplate
@@ -5296,7 +5296,7 @@ public class Notification implements Parcelable
            if (p.mText != null && p.mText.length() != 0
                    && (!showProgress || p.mAllowTextWithProgress)) {
                contentView.setViewVisibility(p.mTextViewId, View.VISIBLE);
                contentView.setTextViewText(p.mTextViewId, processTextSpans(p.mText));
                contentView.setTextViewText(p.mTextViewId, ensureColorSpanContrast(p.mText, p));
                setTextViewColorSecondary(contentView, p.mTextViewId, p);
                hasSecondLine = true;
            } else if (p.mTextViewId != R.id.text) {
@@ -5323,13 +5323,6 @@ public class Notification implements Parcelable
                    RemoteViews.MARGIN_BOTTOM, marginDimen);
        }
        private CharSequence processTextSpans(CharSequence text) {
            if (mInNightMode) {
                return ContrastColorUtil.clearColorSpans(text);
            }
            return text;
        }
        private void setTextViewColorPrimary(RemoteViews contentView, @IdRes int id,
                StandardTemplateParams p) {
            contentView.setTextColor(id, getPrimaryTextColor(p));
@@ -5581,9 +5574,8 @@ public class Notification implements Parcelable
                headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
            }
            if (!TextUtils.isEmpty(headerText)) {
                // TODO: Remove the span entirely to only have the string with propper formating.
                contentView.setTextViewText(R.id.header_text, processTextSpans(
                        processLegacyText(headerText)));
                contentView.setTextViewText(R.id.header_text, ensureColorSpanContrast(
                        processLegacyText(headerText), p));
                setTextViewColorSecondary(contentView, R.id.header_text, p);
                contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
                if (hasTextToLeft) {
@@ -5604,8 +5596,8 @@ public class Notification implements Parcelable
                return false;
            }
            if (!TextUtils.isEmpty(p.mHeaderTextSecondary)) {
                contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
                        processLegacyText(p.mHeaderTextSecondary)));
                contentView.setTextViewText(R.id.header_text_secondary, ensureColorSpanContrast(
                        processLegacyText(p.mHeaderTextSecondary), p));
                setTextViewColorSecondary(contentView, R.id.header_text_secondary, p);
                contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
                if (hasTextToLeft) {
@@ -5846,7 +5838,7 @@ public class Notification implements Parcelable
                big.setViewVisibility(R.id.notification_material_reply_text_1_container,
                        View.VISIBLE);
                big.setTextViewText(R.id.notification_material_reply_text_1,
                        processTextSpans(replyText[0].getText()));
                        ensureColorSpanContrast(replyText[0].getText(), p));
                setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
                big.setViewVisibility(R.id.notification_material_reply_progress,
                        showSpinner ? View.VISIBLE : View.GONE);
@@ -5858,7 +5850,7 @@ public class Notification implements Parcelable
                        && p.maxRemoteInputHistory > 1) {
                    big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
                    big.setTextViewText(R.id.notification_material_reply_text_2,
                            processTextSpans(replyText[1].getText()));
                            ensureColorSpanContrast(replyText[1].getText(), p));
                    setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
                    if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2].getText())
@@ -5866,7 +5858,7 @@ public class Notification implements Parcelable
                        big.setViewVisibility(
                                R.id.notification_material_reply_text_3, View.VISIBLE);
                        big.setTextViewText(R.id.notification_material_reply_text_3,
                                processTextSpans(replyText[2].getText()));
                                ensureColorSpanContrast(replyText[2].getText(), p));
                        setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
                    }
                }
@@ -6280,9 +6272,9 @@ public class Notification implements Parcelable
                                fullLengthColor, notifBackgroundColor);
                    }
                    // Remove full-length color spans and ensure text contrast with the button fill.
                    title = ensureColorSpanContrast(title, buttonFillColor);
                    title = ContrastColorUtil.ensureColorSpanContrast(title, buttonFillColor);
                }
                button.setTextViewText(R.id.action0, processTextSpans(title));
                button.setTextViewText(R.id.action0, ensureColorSpanContrast(title, p));
                int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
                        buttonFillColor, mInNightMode);
                if (tombstone) {
@@ -6307,8 +6299,8 @@ public class Notification implements Parcelable
                    button.setIntDimen(R.id.action0, "setMinimumWidth", minWidthDimen);
                }
            } else {
                button.setTextViewText(R.id.action0, processTextSpans(
                        processLegacyText(action.title)));
                button.setTextViewText(R.id.action0, ensureColorSpanContrast(
                        action.title, p));
                button.setTextColor(R.id.action0, getStandardActionColor(p));
            }
            // CallStyle notifications add action buttons which don't actually exist in mActions,
@@ -6385,72 +6377,12 @@ public class Notification implements Parcelable
         * Ensures contrast on color spans against a background color.
         * Note that any full-length color spans will be removed instead of being contrasted.
         *
         * @param charSequence the charSequence on which the spans are
         * @param background the background color to ensure the contrast against
         * @return the contrasted charSequence
         * @hide
         */
        @VisibleForTesting
        public static CharSequence ensureColorSpanContrast(CharSequence charSequence,
                int background) {
            if (charSequence instanceof Spanned) {
                Spanned ss = (Spanned) charSequence;
                Object[] spans = ss.getSpans(0, ss.length(), Object.class);
                SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
                for (Object span : spans) {
                    Object resultSpan = span;
                    int spanStart = ss.getSpanStart(span);
                    int spanEnd = ss.getSpanEnd(span);
                    boolean fullLength = (spanEnd - spanStart) == charSequence.length();
                    if (resultSpan instanceof CharacterStyle) {
                        resultSpan = ((CharacterStyle) span).getUnderlying();
                    }
                    if (resultSpan instanceof TextAppearanceSpan) {
                        TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
                        ColorStateList textColor = originalSpan.getTextColor();
                        if (textColor != null) {
                            if (fullLength) {
                                // Let's drop the color from the span
                                textColor = null;
                            } else {
                                int[] colors = textColor.getColors();
                                int[] newColors = new int[colors.length];
                                for (int i = 0; i < newColors.length; i++) {
                                    boolean isBgDark = isColorDark(background);
                                    newColors[i] = ContrastColorUtil.ensureLargeTextContrast(
                                            colors[i], background, isBgDark);
                                }
                                textColor = new ColorStateList(textColor.getStates().clone(),
                                        newColors);
                            }
                            resultSpan = new TextAppearanceSpan(
                                    originalSpan.getFamily(),
                                    originalSpan.getTextStyle(),
                                    originalSpan.getTextSize(),
                                    textColor,
                                    originalSpan.getLinkTextColor());
                        }
                    } else if (resultSpan instanceof ForegroundColorSpan) {
                        if (fullLength) {
                            resultSpan = null;
                        } else {
                            ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
                            int foregroundColor = originalSpan.getForegroundColor();
                            boolean isBgDark = isColorDark(background);
                            foregroundColor = ContrastColorUtil.ensureLargeTextContrast(
                                    foregroundColor, background, isBgDark);
                            resultSpan = new ForegroundColorSpan(foregroundColor);
                        }
                    } else {
                        resultSpan = span;
                    }
                    if (resultSpan != null) {
                        builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
                    }
                }
                return builder;
            }
            return charSequence;
        public CharSequence ensureColorSpanContrast(CharSequence charSequence,
                StandardTemplateParams p) {
            return ContrastColorUtil.ensureColorSpanContrast(charSequence, getBackgroundColor(p));
        }
        /**
@@ -7586,8 +7518,8 @@ public class Notification implements Parcelable
            RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(),
                    p, null /* result */);
            if (mSummaryTextSet) {
                contentView.setTextViewText(R.id.text, mBuilder.processTextSpans(
                        mBuilder.processLegacyText(mSummaryText)));
                contentView.setTextViewText(R.id.text, mBuilder.ensureColorSpanContrast(
                        mBuilder.processLegacyText(mSummaryText), p));
                mBuilder.setTextViewColorSecondary(contentView, R.id.text, p);
                contentView.setViewVisibility(R.id.text, View.VISIBLE);
            }
@@ -8207,6 +8139,13 @@ public class Notification implements Parcelable
        @Override
        public void addExtras(Bundle extras) {
            super.addExtras(extras);
            addExtras(extras, false, 0);
        }
        /**
         * @hide
         */
        public void addExtras(Bundle extras, boolean ensureContrast, int backgroundColor) {
            if (mUser != null) {
                // For legacy usages
                extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUser.getName());
@@ -8215,11 +8154,13 @@ public class Notification implements Parcelable
            if (mConversationTitle != null) {
                extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
            }
            if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
                    Message.getBundleArrayForMessages(mMessages));
            if (!mMessages.isEmpty()) {
                extras.putParcelableArray(EXTRA_MESSAGES,
                        getBundleArrayForMessages(mMessages, ensureContrast, backgroundColor));
            }
            if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
                    Message.getBundleArrayForMessages(mHistoricMessages));
            if (!mHistoricMessages.isEmpty()) {
                extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES, getBundleArrayForMessages(
                        mHistoricMessages, ensureContrast, backgroundColor));
            }
            if (mShortcutIcon != null) {
                extras.putParcelable(EXTRA_CONVERSATION_ICON, mShortcutIcon);
@@ -8230,6 +8171,20 @@ public class Notification implements Parcelable
            extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, mIsGroupConversation);
        }
        private static Bundle[] getBundleArrayForMessages(List<Message> messages,
                boolean ensureContrast, int backgroundColor) {
            Bundle[] bundles = new Bundle[messages.size()];
            final int N = messages.size();
            for (int i = 0; i < N; i++) {
                final Message m = messages.get(i);
                if (ensureContrast) {
                    m.ensureColorContrast(backgroundColor);
                }
                bundles[i] = m.toBundle();
            }
            return bundles;
        }
        private void fixTitleAndTextExtras(Bundle extras) {
            Message m = findLatestIncomingMessage();
            CharSequence text = (m == null) ? null : m.mText;
@@ -8441,7 +8396,7 @@ public class Notification implements Parcelable
                mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
            }
            addExtras(mBuilder.mN.extras);
            addExtras(mBuilder.mN.extras, true, mBuilder.getBackgroundColor(p));
            contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
                    mBuilder.getSmallIconColor(p));
            contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor",
@@ -8587,7 +8542,7 @@ public class Notification implements Parcelable
            static final String KEY_EXTRAS_BUNDLE = "extras";
            static final String KEY_REMOTE_INPUT_HISTORY = "remote_input_history";
            private final CharSequence mText;
            private CharSequence mText;
            private final long mTimestamp;
            @Nullable
            private final Person mSender;
@@ -8695,6 +8650,15 @@ public class Notification implements Parcelable
                return this;
            }
            /**
             * Updates TextAppearance spans in the message text so it has sufficient contrast
             * against its background.
             * @hide
             */
            public void ensureColorContrast(int backgroundColor) {
                mText = ContrastColorUtil.ensureColorSpanContrast(mText, backgroundColor);
            }
            /**
             * Get the text to be used for this message, or the fallback text if a type and content
             * Uri have been set
@@ -8788,15 +8752,6 @@ public class Notification implements Parcelable
                return bundle;
            }
            static Bundle[] getBundleArrayForMessages(List<Message> messages) {
                Bundle[] bundles = new Bundle[messages.size()];
                final int N = messages.size();
                for (int i = 0; i < N; i++) {
                    bundles[i] = messages.get(i).toBundle();
                }
                return bundles;
            }
            /**
             * Returns a list of messages read from the given bundle list, e.g.
             * {@link #EXTRA_MESSAGES} or {@link #EXTRA_HISTORIC_MESSAGES}.
@@ -9011,7 +8966,7 @@ public class Notification implements Parcelable
                if (!TextUtils.isEmpty(str)) {
                    contentView.setViewVisibility(rowIds[i], View.VISIBLE);
                    contentView.setTextViewText(rowIds[i],
                            mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
                            mBuilder.ensureColorSpanContrast(mBuilder.processLegacyText(str), p));
                    mBuilder.setTextViewColorSecondary(contentView, rowIds[i], p);
                    contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
                    if (first) {
+88 −0
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ import android.text.style.TextAppearanceSpan;
import android.util.Log;
import android.util.Pair;

import com.android.internal.annotations.VisibleForTesting;

import java.util.Arrays;
import java.util.WeakHashMap;

@@ -280,6 +282,92 @@ public class ContrastColorUtil {
        return charSequence;
    }

    /**
     * Ensures contrast on color spans against a background color.
     * Note that any full-length color spans will be removed instead of being contrasted.
     *
     * @param charSequence the charSequence on which the spans are
     * @param background the background color to ensure the contrast against
     * @return the contrasted charSequence
     */
    public static CharSequence ensureColorSpanContrast(CharSequence charSequence,
            int background) {
        if (charSequence == null) {
            return charSequence;
        }
        if (charSequence instanceof Spanned) {
            Spanned ss = (Spanned) charSequence;
            Object[] spans = ss.getSpans(0, ss.length(), Object.class);
            SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
            for (Object span : spans) {
                Object resultSpan = span;
                int spanStart = ss.getSpanStart(span);
                int spanEnd = ss.getSpanEnd(span);
                boolean fullLength = (spanEnd - spanStart) == charSequence.length();
                if (resultSpan instanceof CharacterStyle) {
                    resultSpan = ((CharacterStyle) span).getUnderlying();
                }
                if (resultSpan instanceof TextAppearanceSpan) {
                    TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
                    ColorStateList textColor = originalSpan.getTextColor();
                    if (textColor != null) {
                        if (fullLength) {
                            // Let's drop the color from the span
                            textColor = null;
                        } else {
                            int[] colors = textColor.getColors();
                            int[] newColors = new int[colors.length];
                            for (int i = 0; i < newColors.length; i++) {
                                boolean isBgDark = isColorDark(background);
                                newColors[i] = ContrastColorUtil.ensureLargeTextContrast(
                                        colors[i], background, isBgDark);
                            }
                            textColor = new ColorStateList(textColor.getStates().clone(),
                                    newColors);
                        }
                        resultSpan = new TextAppearanceSpan(
                                originalSpan.getFamily(),
                                originalSpan.getTextStyle(),
                                originalSpan.getTextSize(),
                                textColor,
                                originalSpan.getLinkTextColor());
                    }
                } else if (resultSpan instanceof ForegroundColorSpan) {
                    if (fullLength) {
                        resultSpan = null;
                    } else {
                        ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
                        int foregroundColor = originalSpan.getForegroundColor();
                        boolean isBgDark = isColorDark(background);
                        foregroundColor = ContrastColorUtil.ensureLargeTextContrast(
                                foregroundColor, background, isBgDark);
                        resultSpan = new ForegroundColorSpan(foregroundColor);
                    }
                } else {
                    resultSpan = span;
                }
                if (resultSpan != null) {
                    builder.setSpan(resultSpan, spanStart, spanEnd, ss.getSpanFlags(span));
                }
            }
            return builder;
        }
        return charSequence;
    }

    /**
     * Determines if the color is light or dark.  Specifically, this is using the same metric as
     * {@link ContrastColorUtil#resolvePrimaryColor(Context, int, boolean)} and peers so that
     * the direction of color shift is consistent.
     *
     * @param color the color to check
     * @return true if the color has higher contrast with white than black
     */
    public static boolean isColorDark(int color) {
        // as per shouldUseDark(), this uses the color contrast midpoint.
        return calculateLuminance(color) <= 0.17912878474;
    }

    private int processColor(int color) {
        return Color.argb(Color.alpha(color),
                255 - Color.red(color),
+0 −88

File changed.

Preview size limit exceeded, changes collapsed.

+107 −1

File changed.

Preview size limit exceeded, changes collapsed.

+2 −1
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.widget.TextView;

import androidx.annotation.ColorInt;

import com.android.internal.util.ContrastColorUtil;
import com.android.keyguard.AlphaOptimizedLinearLayout;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -109,7 +110,7 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout

    public void bind(@Nullable CharSequence title, @Nullable CharSequence text,
            @Nullable View contentView) {
        mTitleView.setText(title);
        mTitleView.setText(title.toString());
        mTitleView.setVisibility(TextUtils.isEmpty(title) ? GONE : VISIBLE);
        if (TextUtils.isEmpty(text)) {
            mTextView.setVisibility(GONE);
Loading