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

Commit fade3095 authored by Matt Casey's avatar Matt Casey
Browse files

Preliminary accessibility support for CropView

Making accessibility nodes for the top and bottom boundaries. They don't
do anything yet, just informational labeling.

Bug: 180062481
Test: Explore UI with switch access and talkback
Change-Id: Icc05fd865c70d32239e955745f1496bbd117529c
parent 36c20253
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -247,6 +247,10 @@
    <string name="screenshot_dismiss_description">Dismiss screenshot</string>
    <!-- Content description indicating that the view is a preview of the screenshot that was just taken [CHAR LIMIT=NONE] -->
    <string name="screenshot_preview_description">Screenshot preview</string>
    <!-- Content description for the top boundary of the screenshot being cropped [CHAR LIMIT=NONE] -->
    <string name="screenshot_top_boundary">Top boundary</string>
    <!-- Content description for the bottom boundary of the screenshot being cropped [CHAR LIMIT=NONE] -->
    <string name="screenshot_bottom_boundary">Bottom boundary</string>

    <!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
    <string name="screenrecord_name">Screen Recorder</string>
+89 −0
Original line number Diff line number Diff line
@@ -24,15 +24,21 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.Log;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;

import androidx.annotation.Nullable;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;

import com.android.internal.widget.ExploreByTouchHelper;
import com.android.systemui.R;

/**
@@ -80,6 +86,8 @@ public class CropView extends View {
        t.recycle();
        // 48 dp touchable region around each handle.
        mCropTouchMargin = 24 * getResources().getDisplayMetrics().density;

        setAccessibilityDelegate(new AccessibilityHelper());
    }

    @Override
@@ -231,6 +239,87 @@ public class CropView extends View {
        return CropBoundary.NONE;
    }

    private class AccessibilityHelper extends ExploreByTouchHelper {

        private static final int TOP_HANDLE_ID = 1;
        private static final int BOTTOM_HANDLE_ID = 2;

        AccessibilityHelper() {
            super(CropView.this);
        }

        @Override
        protected int getVirtualViewAt(float x, float y) {
            if (Math.abs(y - fractionToPixels(mTopCrop)) < mCropTouchMargin) {
                return TOP_HANDLE_ID;
            }
            if (Math.abs(y - fractionToPixels(mBottomCrop)) < mCropTouchMargin) {
                return BOTTOM_HANDLE_ID;
            }
            return ExploreByTouchHelper.INVALID_ID;
        }

        @Override
        protected void getVisibleVirtualViews(IntArray virtualViewIds) {
            virtualViewIds.add(TOP_HANDLE_ID);
            virtualViewIds.add(BOTTOM_HANDLE_ID);
        }

        @Override
        protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
            switch(virtualViewId) {
                case TOP_HANDLE_ID:
                    event.setContentDescription(
                            getResources().getString(R.string.screenshot_top_boundary));
                    break;
                case BOTTOM_HANDLE_ID:
                    event.setContentDescription(
                            getResources().getString(R.string.screenshot_bottom_boundary));
                    break;
            }
        }

        @Override
        protected void onPopulateNodeForVirtualView(int virtualViewId,
                AccessibilityNodeInfo node) {
            switch(virtualViewId) {
                case TOP_HANDLE_ID:
                    node.setContentDescription(
                            getResources().getString(R.string.screenshot_top_boundary));
                    setNodePositions(mTopCrop, node);
                    break;
                case BOTTOM_HANDLE_ID:
                    node.setContentDescription(
                            getResources().getString(R.string.screenshot_bottom_boundary));
                    setNodePositions(mBottomCrop, node);
                    break;
            }

            // TODO: need to figure out the full set of actions to support here.
            node.addAction(
                    AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
            node.setClickable(true);
            node.setFocusable(true);
        }

        @Override
        protected boolean onPerformActionForVirtualView(
                int virtualViewId, int action, Bundle arguments) {
            return false;
        }

        private void setNodePositions(float fraction, AccessibilityNodeInfo node) {
            int pixels = fractionToPixels(fraction);
            Rect rect = new Rect(0, (int) (pixels - mCropTouchMargin),
                    getWidth(), (int) (pixels + mCropTouchMargin));
            node.setBoundsInParent(rect);
            int[] pos = new int[2];
            getLocationOnScreen(pos);
            rect.offset(pos[0], pos[1]);
            node.setBoundsInScreen(rect);
        }
    }

    /**
     * Listen for crop motion events and state.
     */