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

Commit cbc43ed9 authored by Phil Weaver's avatar Phil Weaver Committed by Android (Google) Code Review
Browse files

Merge "Make accessibility window events more granular"

parents 5fdf3d09 bb2f28a7
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -47781,6 +47781,7 @@ package android.view.accessibility {
    method public java.lang.CharSequence getPackageName();
    method public android.view.accessibility.AccessibilityRecord getRecord(int);
    method public int getRecordCount();
    method public int getWindowChanges();
    method public void initFromParcel(android.os.Parcel);
    method public static android.view.accessibility.AccessibilityEvent obtain(int);
    method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
@@ -47825,6 +47826,17 @@ package android.view.accessibility {
    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
    field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
    field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20
    field public static final int WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 128; // 0x80
    field public static final int WINDOWS_CHANGE_ACTIVE = 32; // 0x20
    field public static final int WINDOWS_CHANGE_ADDED = 1; // 0x1
    field public static final int WINDOWS_CHANGE_BOUNDS = 8; // 0x8
    field public static final int WINDOWS_CHANGE_CHILDREN = 512; // 0x200
    field public static final int WINDOWS_CHANGE_FOCUSED = 64; // 0x40
    field public static final int WINDOWS_CHANGE_LAYER = 16; // 0x10
    field public static final int WINDOWS_CHANGE_PARENT = 256; // 0x100
    field public static final int WINDOWS_CHANGE_PIP = 1024; // 0x400
    field public static final int WINDOWS_CHANGE_REMOVED = 2; // 0x2
    field public static final int WINDOWS_CHANGE_TITLE = 4; // 0x4
  }
  public abstract interface AccessibilityEventSource {
+156 −9
Original line number Diff line number Diff line
@@ -350,21 +350,22 @@ import java.util.List;
 * view.</br>
 * </p>
 * <p>
 * <b>Windows changed</b> - represents the event of changes in the windows shown on
 * <b>Windows changed</b> - represents a change in the windows shown on
 * the screen such as a window appeared, a window disappeared, a window size changed,
 * a window layer changed, etc.</br>
 * a window layer changed, etc. These events should only come from the system, which is responsible
 * for managing windows. For regions of the user interface that are presented as windows but are
 * controlled by an app's process, use {@link #TYPE_WINDOW_STATE_CHANGED}.</br>
 * <em>Type:</em> {@link #TYPE_WINDOWS_CHANGED}</br>
 * <em>Properties:</em></br>
 * <ul>
 *   <li>{@link #getEventType()} - The type of the event.</li>
 *   <li>{@link #getEventTime()} - The event time.</li>
 *   <li>{@link #getWindowChanges()}</li> - The specific change to the source window
 * </ul>
 * <em>Note:</em> You can retrieve the {@link AccessibilityWindowInfo} for the window
 * source of the event via {@link AccessibilityEvent#getSource()} to get the source
 * node on which then call {@link AccessibilityNodeInfo#getWindow()
 * AccessibilityNodeInfo.getWindow()} to get the window. Also all windows on the screen can
 * be retrieved by a call to {@link android.accessibilityservice.AccessibilityService#getWindows()
 * android.accessibilityservice.AccessibilityService.getWindows()}.
 * source of the event by looking through the list returned by
 * {@link android.accessibilityservice.AccessibilityService#getWindows()} for the window whose ID
 * matches {@link #getWindowId()}.
 * </p>
 * <p>
 * <b>NOTIFICATION TYPES</b></br>
@@ -712,6 +713,88 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
     */
    public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window was added.
     */
    public static final int WINDOWS_CHANGE_ADDED = 0x00000001;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * A window was removed.
     */
    public static final int WINDOWS_CHANGE_REMOVED = 0x00000002;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window's title changed.
     */
    public static final int WINDOWS_CHANGE_TITLE = 0x00000004;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window's bounds changed.
     */
    public static final int WINDOWS_CHANGE_BOUNDS = 0x00000008;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window's layer changed.
     */
    public static final int WINDOWS_CHANGE_LAYER = 0x00000010;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window's {@link AccessibilityWindowInfo#isActive()} changed.
     */
    public static final int WINDOWS_CHANGE_ACTIVE = 0x00000020;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window's {@link AccessibilityWindowInfo#isFocused()} changed.
     */
    public static final int WINDOWS_CHANGE_FOCUSED = 0x00000040;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window's {@link AccessibilityWindowInfo#isAccessibilityFocused()} changed.
     */
    public static final int WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 0x00000080;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window's parent changed.
     */
    public static final int WINDOWS_CHANGE_PARENT = 0x00000100;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window's children changed.
     */
    public static final int WINDOWS_CHANGE_CHILDREN = 0x00000200;

    /**
     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
     * The window either entered or exited picture-in-picture mode.
     */
    public static final int WINDOWS_CHANGE_PIP = 0x00000400;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = { "WINDOWS_CHANGE_" }, value = {
            WINDOWS_CHANGE_ADDED,
            WINDOWS_CHANGE_REMOVED,
            WINDOWS_CHANGE_TITLE,
            WINDOWS_CHANGE_BOUNDS,
            WINDOWS_CHANGE_LAYER,
            WINDOWS_CHANGE_ACTIVE,
            WINDOWS_CHANGE_FOCUSED,
            WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED,
            WINDOWS_CHANGE_PARENT,
            WINDOWS_CHANGE_CHILDREN,
            WINDOWS_CHANGE_PIP
    })
    public @interface WindowsChangeTypes {}

    /** @hide */
    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
@@ -782,6 +865,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
    int mMovementGranularity;
    int mAction;
    int mContentChangeTypes;
    int mWindowChangeTypes;

    private ArrayList<AccessibilityRecord> mRecords;

@@ -802,6 +886,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
        mMovementGranularity = event.mMovementGranularity;
        mAction = event.mAction;
        mContentChangeTypes = event.mContentChangeTypes;
        mWindowChangeTypes = event.mWindowChangeTypes;
        mEventTime = event.mEventTime;
        mPackageName = event.mPackageName;
    }
@@ -918,6 +1003,43 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
        mContentChangeTypes = changeTypes;
    }

    /**
     * Get the bit mask of change types signaled by a {@link #TYPE_WINDOWS_CHANGED} event. A
     * single event may represent multiple change types.
     *
     * @return The bit mask of change types.
     */
    @WindowsChangeTypes
    public int getWindowChanges() {
        return mWindowChangeTypes;
    }

    /** @hide  */
    public void setWindowChanges(@WindowsChangeTypes int changes) {
        mWindowChangeTypes = changes;
    }

    private static String windowChangeTypesToString(@WindowsChangeTypes int types) {
        return BitUtils.flagsToString(types, AccessibilityEvent::singleWindowChangeTypeToString);
    }

    private static String singleWindowChangeTypeToString(int type) {
        switch (type) {
            case WINDOWS_CHANGE_ADDED: return "WINDOWS_CHANGE_ADDED";
            case WINDOWS_CHANGE_REMOVED: return "WINDOWS_CHANGE_REMOVED";
            case WINDOWS_CHANGE_TITLE: return "WINDOWS_CHANGE_TITLE";
            case WINDOWS_CHANGE_BOUNDS: return "WINDOWS_CHANGE_BOUNDS";
            case WINDOWS_CHANGE_LAYER: return "WINDOWS_CHANGE_LAYER";
            case WINDOWS_CHANGE_ACTIVE: return "WINDOWS_CHANGE_ACTIVE";
            case WINDOWS_CHANGE_FOCUSED: return "WINDOWS_CHANGE_FOCUSED";
            case WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED:
                return "WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED";
            case WINDOWS_CHANGE_PARENT: return "WINDOWS_CHANGE_PARENT";
            case WINDOWS_CHANGE_CHILDREN: return "WINDOWS_CHANGE_CHILDREN";
            default: return Integer.toHexString(type);
        }
    }

    /**
     * Sets the event type.
     *
@@ -1024,6 +1146,26 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
        return mAction;
    }

    /**
     * Convenience method to obtain a {@link #TYPE_WINDOWS_CHANGED} event for a specific window and
     * change set.
     *
     * @param windowId The ID of the window that changed
     * @param windowChangeTypes The changes to populate
     * @return An instance of a TYPE_WINDOWS_CHANGED, populated with the requested fields and with
     *         importantForAccessibility set to {@code true}.
     *
     * @hide
     */
    public static AccessibilityEvent obtainWindowsChangedEvent(
            int windowId, int windowChangeTypes) {
        final AccessibilityEvent event = AccessibilityEvent.obtain(TYPE_WINDOWS_CHANGED);
        event.setWindowId(windowId);
        event.setWindowChanges(windowChangeTypes);
        event.setImportantForAccessibility(true);
        return event;
    }

    /**
     * Returns a cached instance if such is available or a new one is
     * instantiated with its type property set.
@@ -1099,6 +1241,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
        mMovementGranularity = 0;
        mAction = 0;
        mContentChangeTypes = 0;
        mWindowChangeTypes = 0;
        mPackageName = null;
        mEventTime = 0;
        if (mRecords != null) {
@@ -1120,6 +1263,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
        mMovementGranularity = parcel.readInt();
        mAction = parcel.readInt();
        mContentChangeTypes = parcel.readInt();
        mWindowChangeTypes = parcel.readInt();
        mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
        mEventTime = parcel.readLong();
        mConnectionId = parcel.readInt();
@@ -1178,6 +1322,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
        parcel.writeInt(mMovementGranularity);
        parcel.writeInt(mAction);
        parcel.writeInt(mContentChangeTypes);
        parcel.writeInt(mWindowChangeTypes);
        TextUtils.writeToParcel(mPackageName, parcel, 0);
        parcel.writeLong(mEventTime);
        parcel.writeInt(mConnectionId);
@@ -1238,11 +1383,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
        builder.append("; PackageName: ").append(mPackageName);
        builder.append("; MovementGranularity: ").append(mMovementGranularity);
        builder.append("; Action: ").append(mAction);
        builder.append("; ContentChangeTypes: ").append(
                contentChangeTypesToString(mContentChangeTypes));
        builder.append("; WindowChangeTypes: ").append(
                windowChangeTypesToString(mWindowChangeTypes));
        builder.append(super.toString());
        if (DEBUG) {
            builder.append("\n");
            builder.append("; ContentChangeTypes: ").append(
                    contentChangeTypesToString(mContentChangeTypes));
            builder.append("; sourceWindowId: ").append(mSourceWindowId);
            builder.append("; mSourceNodeId: ").append(mSourceNodeId);
            for (int i = 0; i < getRecordCount(); i++) {
+58 −1
Original line number Diff line number Diff line
@@ -21,9 +21,12 @@ import android.annotation.TestApi;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.LongArray;
import android.util.Pools.SynchronizedPool;
import android.view.accessibility.AccessibilityEvent.WindowsChangeTypes;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

/**
@@ -575,7 +578,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
        StringBuilder builder = new StringBuilder();
        builder.append("AccessibilityWindowInfo[");
        builder.append("title=").append(mTitle);
        builder.append("id=").append(mId);
        builder.append(", id=").append(mId);
        builder.append(", type=").append(typeToString(mType));
        builder.append(", layer=").append(mLayer);
        builder.append(", bounds=").append(mBoundsInScreen);
@@ -713,6 +716,60 @@ public final class AccessibilityWindowInfo implements Parcelable {
        return false;
    }

    /**
     * Reports how this window differs from a possibly different state of the same window. The
     * argument must have the same id and type as neither of those properties may change.
     *
     * @param other The new state.
     * @return A set of flags showing how the window has changes, or 0 if the two states are the
     * same.
     *
     * @hide
     */
    @WindowsChangeTypes
    public int differenceFrom(AccessibilityWindowInfo other) {
        if (other.mId != mId) {
            throw new IllegalArgumentException("Not same window.");
        }
        if (other.mType != mType) {
            throw new IllegalArgumentException("Not same type.");
        }
        int changes = 0;
        if (!TextUtils.equals(mTitle, other.mTitle)) {
            changes |= AccessibilityEvent.WINDOWS_CHANGE_TITLE;
        }

        if (!mBoundsInScreen.equals(other.mBoundsInScreen)) {
            changes |= AccessibilityEvent.WINDOWS_CHANGE_BOUNDS;
        }
        if (mLayer != other.mLayer) {
            changes |= AccessibilityEvent.WINDOWS_CHANGE_LAYER;
        }
        if (getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)
                != other.getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)) {
            changes |= AccessibilityEvent.WINDOWS_CHANGE_ACTIVE;
        }
        if (getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)
                != other.getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)) {
            changes |= AccessibilityEvent.WINDOWS_CHANGE_FOCUSED;
        }
        if (getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)
                != other.getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)) {
            changes |= AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
        }
        if (getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)
                != other.getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)) {
            changes |= AccessibilityEvent.WINDOWS_CHANGE_PIP;
        }
        if (mParentId != other.mParentId) {
            changes |= AccessibilityEvent.WINDOWS_CHANGE_PARENT;
        }
        if (!Objects.equals(mChildIds, other.mChildIds)) {
            changes |= AccessibilityEvent.WINDOWS_CHANGE_CHILDREN;
        }
        return changes;
    }

    public static final Parcelable.Creator<AccessibilityWindowInfo> CREATOR =
            new Creator<AccessibilityWindowInfo>() {
        @Override
+68 −0
Original line number Diff line number Diff line
/*
 * Copyright 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view.accessibility;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

import android.os.Parcel;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * AccessibilityEvent is public, so CTS covers it pretty well. Verifying hidden methods here.
 */
@RunWith(AndroidJUnit4.class)
public class AccessibilityEventTest {
    @Test
    public void testImportantForAccessibiity_getSetWorkAcrossParceling() {
        AccessibilityEvent event = AccessibilityEvent.obtain();
        event.setImportantForAccessibility(true);
        assertTrue(copyEventViaParcel(event).isImportantForAccessibility());

        event.setImportantForAccessibility(false);
        assertFalse(copyEventViaParcel(event).isImportantForAccessibility());
    }

    @Test
    public void testSouceNodeId_getSetWorkAcrossParceling() {
        final long sourceNodeId = 0x1234567890ABCDEFL;
        AccessibilityEvent event = AccessibilityEvent.obtain();
        event.setSourceNodeId(sourceNodeId);
        assertEquals(sourceNodeId, copyEventViaParcel(event).getSourceNodeId());
    }

    @Test
    public void testWindowChanges_getSetWorkAcrossParceling() {
        final int windowChanges = AccessibilityEvent.WINDOWS_CHANGE_TITLE
                | AccessibilityEvent.WINDOWS_CHANGE_ACTIVE
                | AccessibilityEvent.WINDOWS_CHANGE_FOCUSED;
        AccessibilityEvent event = AccessibilityEvent.obtain();
        event.setWindowChanges(windowChanges);
        assertEquals(windowChanges, copyEventViaParcel(event).getWindowChanges());
    }

    private AccessibilityEvent copyEventViaParcel(AccessibilityEvent event) {
        Parcel parcel = Parcel.obtain();
        event.writeToParcel(parcel, 0);
        parcel.setDataPosition(0);
        return AccessibilityEvent.CREATOR.createFromParcel(parcel);
    }
}
+110 −44

File changed.

Preview size limit exceeded, changes collapsed.

Loading