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

Commit 1e0585f3 authored by Yorke Lee's avatar Yorke Lee
Browse files

Make switch access work for incoming call screen

An accessibility click on the glowpad now exposes the touch targets
as virtual views to accessibility services. Performing an accessibility
click on each virtual view performs the same function as swiping towards
that target.

Mark contact photo as unimportant for accessibility, it doesn't provide
any useful context or functionality.

Bug: 19075527
Change-Id: I2e980394175bf77379f5cb99126dd5970e0f0555
parent 4019d637
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@
            android:layout_gravity="center_vertical"
            android:gravity="top|center_horizontal"
            android:scaleType="centerCrop"
            android:contentDescription="@string/contactPhoto"
            android:importantForAccessibility="no"
            android:background="@android:color/white"
            android:src="@drawable/img_no_image_automirrored" />

+115 −0
Original line number Diff line number Diff line
@@ -29,9 +29,14 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Vibrator;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.widget.ExploreByTouchHelper;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -39,11 +44,16 @@ import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;

import com.android.incallui.R;

import java.util.ArrayList;
import java.util.List;

/**
 * This is a copy of com.android.internal.widget.multiwaveview.GlowPadView with minor changes
@@ -124,6 +134,9 @@ public class GlowPadView extends View {
    private boolean mDragging;
    private int mNewTargetResources;

    private AccessibilityNodeProvider mAccessibilityNodeProvider;
    private GlowpadExploreByTouchHelper mExploreByTouchHelper;

    private class AnimationBundle extends ArrayList<Tweener> {
        private static final long serialVersionUID = 0xA84D78726F127468L;
        private boolean mSuspended;
@@ -274,6 +287,9 @@ public class GlowPadView extends View {
        mPointCloud = new PointCloud(pointDrawable);
        mPointCloud.makePointCloud(mInnerRadius, mOuterRadius);
        mPointCloud.glowManager.setRadius(mGlowRadius);

        mExploreByTouchHelper = new GlowpadExploreByTouchHelper(this);
        ViewCompat.setAccessibilityDelegate(this, mExploreByTouchHelper);
    }

    private int getResourceId(TypedArray a, int id) {
@@ -1355,4 +1371,103 @@ public class GlowPadView extends View {
        }
        return replaced;
    }

    public class GlowpadExploreByTouchHelper extends ExploreByTouchHelper {

        private Rect mBounds = new Rect();

        public GlowpadExploreByTouchHelper(View forView) {
            super(forView);
        }

        @Override
        protected int getVirtualViewAt(float x, float y) {
            if (mGrabbedState == OnTriggerListener.CENTER_HANDLE) {
                for (int i = 0; i < mTargetDrawables.size(); i++) {
                    final TargetDrawable target = mTargetDrawables.get(i);
                    if (target.isEnabled() && target.getBounds().contains((int) x, (int) y)) {
                        return i;
                    }
                }
                return INVALID_ID;
            } else {
                return HOST_ID;
            }
        }

        @Override
        protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
            if (mGrabbedState == OnTriggerListener.CENTER_HANDLE) {
                // Add virtual views backwards so that accessibility services like switch
                // access traverse them in the correct order
                for (int i = mTargetDrawables.size() - 1; i >= 0; i--) {
                    if (mTargetDrawables.get(i).isEnabled()) {
                        virtualViewIds.add(i);
                    }
                }
            }
        }

        @Override
        protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
            if (virtualViewId >= 0 && virtualViewId < mTargetDescriptions.size()) {
                event.setContentDescription(mTargetDescriptions.get(virtualViewId));
            }
        }

        @Override
        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
            if (host == GlowPadView.this && event.getEventType()
                    == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
                event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
            }
            super.onInitializeAccessibilityEvent(host, event);
        }

        @Override
        public void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) {
            if (mGrabbedState == OnTriggerListener.NO_HANDLE) {
                node.setClickable(true);
                node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
            }
            mBounds.set(0, 0, GlowPadView.this.getWidth(), GlowPadView.this.getHeight());
            node.setBoundsInParent(mBounds);
        }

        @Override
        public boolean performAccessibilityAction(View host, int action, Bundle args) {
            if (mGrabbedState == OnTriggerListener.NO_HANDLE) {
                // Simulate handle being grabbed to expose targets.
                trySwitchToFirstTouchState(mWaveCenterX, mWaveCenterY);
                invalidateRoot();
                return true;
            }
            return super.performAccessibilityAction(host, action, args);
        }

        @Override
        protected void onPopulateNodeForVirtualView(int virtualViewId,
                AccessibilityNodeInfoCompat node) {
            if (virtualViewId < mTargetDrawables.size()) {
                final TargetDrawable target = mTargetDrawables.get(virtualViewId);
                node.setBoundsInParent(target.getBounds());
                node.setClickable(true);
                node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
                node.setContentDescription(getTargetDescription(virtualViewId));
            }
        }

        @Override
        protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
                Bundle arguments) {
            if (action == AccessibilityNodeInfo.ACTION_CLICK) {
                if (virtualViewId >= 0 && virtualViewId < mTargetDrawables.size()) {
                    dispatchTriggerEvent(virtualViewId);
                    return true;
                }
            }
            return false;
        }

    }
}
+13 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.incallui.widget.multiwaveview;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.util.Log;
@@ -46,6 +47,7 @@ public class TargetDrawable {
    private boolean mEnabled = true;
    private final int mResourceId;
    private int mNumDrawables = 1;
    private Rect mBounds;

    /**
     * This is changed from the framework version to pass in the number of drawables in the
@@ -214,6 +216,17 @@ public class TargetDrawable {
        return mDrawable != null ? mDrawable.getIntrinsicHeight() : 0;
    }

    public Rect getBounds() {
        if (mBounds == null) {
            mBounds = new Rect();
        }
        mBounds.set((int) (mTranslationX + mPositionX - getWidth() * 0.5),
                (int) (mTranslationY + mPositionY - getHeight() * 0.5),
                (int) (mTranslationX + mPositionX + getWidth() * 0.5),
                (int) (mTranslationY + mPositionY + getHeight() * 0.5));
        return mBounds;
    }

    public void draw(Canvas canvas) {
        if (mDrawable == null || !mEnabled) {
            return;