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

Commit c355c7a9 authored by Phil Weaver's avatar Phil Weaver
Browse files

Fix accessibility reporting for lock screen PIN

Now reporting what is shown on the screen and generating
events when that changes as dots replace the numbers.

Bug: 69454936
Test: Manually observing the event stream as I enter a PIN
on the lock screen.

Change-Id: I4c0afe1140006192b57febb500ea982976645652
parent be8a0141
Loading
Loading
Loading
Loading
+48 −5
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@@ -78,6 +79,8 @@ public class PasswordTextView extends View {
     */
    private static final float OVERSHOOT_TIME_POSITION = 0.5f;

    private static char DOT = '\u2022';

    /**
     * The raw text size, will be multiplied by the scaled density when drawn
     */
@@ -208,7 +211,7 @@ public class PasswordTextView extends View {

    public void append(char c) {
        int visibleChars = mTextChars.size();
        String textbefore = mText;
        CharSequence textbefore = getTransformedText();
        mText = mText + c;
        int newLength = mText.length();
        CharState charState;
@@ -245,7 +248,7 @@ public class PasswordTextView extends View {

    public void deleteLastChar() {
        int length = mText.length();
        String textbefore = mText;
        CharSequence textbefore = getTransformedText();
        if (length > 0) {
            mText = mText.substring(0, length - 1);
            CharState charState = mTextChars.get(length - 1);
@@ -259,6 +262,21 @@ public class PasswordTextView extends View {
        return mText;
    }

    private CharSequence getTransformedText() {
        int textLength = mTextChars.size();
        StringBuilder stringBuilder = new StringBuilder(textLength);
        for (int i = 0; i < textLength; i++) {
            CharState charState = mTextChars.get(i);
            // If the dot is disappearing, the character is disappearing entirely. Consider
            // it gone.
            if (charState.dotAnimator != null && !charState.dotAnimationIsGrowing) {
                continue;
            }
            stringBuilder.append(charState.isCharVisibleForA11y() ? charState.whichChar : DOT);
        }
        return stringBuilder;
    }

    private CharState obtainCharState(char c) {
        CharState charState;
        if(mCharPool.isEmpty()) {
@@ -272,7 +290,7 @@ public class PasswordTextView extends View {
    }

    public void reset(boolean animated, boolean announce) {
        String textbefore = mText;
        CharSequence textbefore = getTransformedText();
        mText = "";
        int length = mTextChars.size();
        int middleIndex = (length - 1) / 2;
@@ -305,7 +323,7 @@ public class PasswordTextView extends View {
        }
    }

    void sendAccessibilityEventTypeViewTextChanged(String beforeText, int fromIndex,
    void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex,
                                                   int removedCount, int addedCount) {
        if (AccessibilityManager.getInstance(mContext).isEnabled() &&
                (isFocused() || isSelected() && isShown())) {
@@ -315,6 +333,10 @@ public class PasswordTextView extends View {
            event.setRemovedCount(removedCount);
            event.setAddedCount(addedCount);
            event.setBeforeText(beforeText);
            CharSequence transformedText = getTransformedText();
            if (!TextUtils.isEmpty(transformedText)) {
                event.getText().add(transformedText);
            }
            event.setPassword(true);
            sendAccessibilityEventUnchecked(event);
        }
@@ -332,8 +354,9 @@ public class PasswordTextView extends View {
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);

        info.setClassName(PasswordTextView.class.getName());
        info.setClassName(EditText.class.getName());
        info.setPassword(true);
        info.setText(getTransformedText());

        info.setEditable(true);

@@ -420,7 +443,19 @@ public class PasswordTextView extends View {
                = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                boolean textVisibleBefore = isCharVisibleForA11y();
                float beforeTextSizeFactor = currentTextSizeFactor;
                currentTextSizeFactor = (float) animation.getAnimatedValue();
                if (textVisibleBefore != isCharVisibleForA11y()) {
                    currentTextSizeFactor = beforeTextSizeFactor;
                    CharSequence beforeText = getTransformedText();
                    currentTextSizeFactor = (float) animation.getAnimatedValue();
                    int indexOfThisChar = mTextChars.indexOf(CharState.this);
                    if (indexOfThisChar >= 0) {
                        sendAccessibilityEventTypeViewTextChanged(
                                beforeText, indexOfThisChar, 1, 1);
                    }
                }
                invalidate();
            }
        };
@@ -673,5 +708,13 @@ public class PasswordTextView extends View {
            }
            return charWidth + mCharPadding * currentWidthFactor;
        }

        public boolean isCharVisibleForA11y() {
            // The text has size 0 when it is first added, but we want to count it as visible if
            // it will become visible presently. Count text as visible if an animator
            // is configured to make it grow.
            boolean textIsGrowing = textAnimator != null && textAnimationIsGrowing;
            return (currentTextSizeFactor > 0) || textIsGrowing;
        }
    }
}