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

Commit ffb46bf2 authored by Alan Viverette's avatar Alan Viverette
Browse files

Add support for Explore by Touch to RadialTimePickerView

Also adds IntArray, which is like LongArray for integers, and prevents
the AM/PM label text in the time picker header from wrapping.

BUG: 17468036
Change-Id: I7120089885709f23e20368927e4b3ed9db2e5393
parent fa9ed8ca
Loading
Loading
Loading
Loading
+162 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2014 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.util;

import com.android.internal.util.ArrayUtils;

import libcore.util.EmptyArray;

/**
 * Implements a growing array of int primitives.
 *
 * @hide
 */
public class IntArray implements Cloneable {
    private static final int MIN_CAPACITY_INCREMENT = 12;

    private int[] mValues;
    private int mSize;

    /**
     * Creates an empty IntArray with the default initial capacity.
     */
    public IntArray() {
        this(10);
    }

    /**
     * Creates an empty IntArray with the specified initial capacity.
     */
    public IntArray(int initialCapacity) {
        if (initialCapacity == 0) {
            mValues = EmptyArray.INT;
        } else {
            mValues = ArrayUtils.newUnpaddedIntArray(initialCapacity);
        }
        mSize = 0;
    }

    /**
     * Appends the specified value to the end of this array.
     */
    public void add(int value) {
        add(mSize, value);
    }

    /**
     * Inserts a value at the specified position in this array.
     *
     * @throws IndexOutOfBoundsException when index < 0 || index > size()
     */
    public void add(int index, int value) {
        if (index < 0 || index > mSize) {
            throw new IndexOutOfBoundsException();
        }

        ensureCapacity(1);

        if (mSize - index != 0) {
            System.arraycopy(mValues, index, mValues, index + 1, mSize - index);
        }

        mValues[index] = value;
        mSize++;
    }

    /**
     * Adds the values in the specified array to this array.
     */
    public void addAll(IntArray values) {
        final int count = values.mSize;
        ensureCapacity(count);

        System.arraycopy(values.mValues, 0, mValues, mSize, count);
        mSize += count;
    }

    /**
     * Ensures capacity to append at least <code>count</code> values.
     */
    private void ensureCapacity(int count) {
        final int currentSize = mSize;
        final int minCapacity = currentSize + count;
        if (minCapacity >= mValues.length) {
            final int targetCap = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2) ?
                    MIN_CAPACITY_INCREMENT : currentSize >> 1);
            final int newCapacity = targetCap > minCapacity ? targetCap : minCapacity;
            final int[] newValues = ArrayUtils.newUnpaddedIntArray(newCapacity);
            System.arraycopy(mValues, 0, newValues, 0, currentSize);
            mValues = newValues;
        }
    }

    /**
     * Removes all values from this array.
     */
    public void clear() {
        mSize = 0;
    }

    @Override
    public IntArray clone() throws CloneNotSupportedException {
        final IntArray clone = (IntArray) super.clone();
        clone.mValues = mValues.clone();
        return clone;
    }

    /**
     * Returns the value at the specified position in this array.
     */
    public int get(int index) {
        if (index >= mSize) {
            throw new ArrayIndexOutOfBoundsException(mSize, index);
        }
        return mValues[index];
    }

    /**
     * Returns the index of the first occurrence of the specified value in this
     * array, or -1 if this array does not contain the value.
     */
    public int indexOf(int value) {
        final int n = mSize;
        for (int i = 0; i < n; i++) {
            if (mValues[i] == value) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Removes the value at the specified index from this array.
     */
    public void remove(int index) {
        if (index >= mSize) {
            throw new ArrayIndexOutOfBoundsException(mSize, index);
        }
        System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
        mSize--;
    }

    /**
     * Returns the number of values in this array.
     */
    public int size() {
        return mSize;
    }
}
+396 −142

File changed.

Preview size limit exceeded, changes collapsed.

+2 −1
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@ import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.text.format.Time;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.MathUtils;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View;
@@ -610,7 +611,7 @@ class SimpleMonthView extends View {
        }
        }


        @Override
        @Override
        protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
        protected void getVisibleVirtualViews(IntArray virtualViewIds) {
            for (int day = 1; day <= mNumCells; day++) {
            for (int day = 1; day <= mNumCells; day++) {
                virtualViewIds.add(day);
                virtualViewIds.add(day);
            }
            }
+5 −15
Original line number Original line Diff line number Diff line
@@ -611,15 +611,12 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl
            if (mAllowAutoAdvance && autoAdvance) {
            if (mAllowAutoAdvance && autoAdvance) {
                updateHeaderHour(newValue, false);
                updateHeaderHour(newValue, false);
                setCurrentItemShowing(MINUTE_INDEX, true, false);
                setCurrentItemShowing(MINUTE_INDEX, true, false);
                mRadialTimePickerView.announceForAccessibility(newValue + ". " + mSelectMinutes);
                mDelegator.announceForAccessibility(newValue + ". " + mSelectMinutes);
            } else {
            } else {
                updateHeaderHour(newValue, true);
                updateHeaderHour(newValue, true);
                mRadialTimePickerView.setContentDescription(
                        mHourPickerDescription + ": " + newValue);
            }
            }
        } else if (pickerIndex == MINUTE_INDEX){
        } else if (pickerIndex == MINUTE_INDEX){
            updateHeaderMinute(newValue, true);
            updateHeaderMinute(newValue, true);
            mRadialTimePickerView.setContentDescription(mMinutePickerDescription + ": " + newValue);
        } else if (pickerIndex == AMPM_INDEX) {
        } else if (pickerIndex == AMPM_INDEX) {
            updateAmPmLabelStates(newValue);
            updateAmPmLabelStates(newValue);
        } else if (pickerIndex == ENABLE_PICKER_INDEX) {
        } else if (pickerIndex == ENABLE_PICKER_INDEX) {
@@ -744,19 +741,12 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl
        mRadialTimePickerView.setCurrentItemShowing(index, animateCircle);
        mRadialTimePickerView.setCurrentItemShowing(index, animateCircle);


        if (index == HOUR_INDEX) {
        if (index == HOUR_INDEX) {
            int hours = mRadialTimePickerView.getCurrentHour();
            if (!mIs24HourView) {
                hours = hours % 12;
            }
            mRadialTimePickerView.setContentDescription(mHourPickerDescription + ": " + hours);
            if (announce) {
            if (announce) {
                mRadialTimePickerView.announceForAccessibility(mSelectHours);
                mDelegator.announceForAccessibility(mSelectHours);
            }
            }
        } else {
        } else {
            int minutes = mRadialTimePickerView.getCurrentMinute();
            mRadialTimePickerView.setContentDescription(mMinutePickerDescription + ": " + minutes);
            if (announce) {
            if (announce) {
                mRadialTimePickerView.announceForAccessibility(mSelectMinutes);
                mDelegator.announceForAccessibility(mSelectMinutes);
            }
            }
        }
        }


@@ -789,7 +779,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl
                    } else {
                    } else {
                        deletedKeyStr = String.format("%d", getValFromKeyCode(deleted));
                        deletedKeyStr = String.format("%d", getValFromKeyCode(deleted));
                    }
                    }
                    mRadialTimePickerView.announceForAccessibility(
                    mDelegator.announceForAccessibility(
                            String.format(mDeletedKeyFormat, deletedKeyStr));
                            String.format(mDeletedKeyFormat, deletedKeyStr));
                    updateDisplay(true);
                    updateDisplay(true);
                }
                }
@@ -851,7 +841,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl
        }
        }


        int val = getValFromKeyCode(keyCode);
        int val = getValFromKeyCode(keyCode);
        mRadialTimePickerView.announceForAccessibility(String.format("%d", val));
        mDelegator.announceForAccessibility(String.format("%d", val));
        // Automatically fill in 0's if AM or PM was legally entered.
        // Automatically fill in 0's if AM or PM was legally entered.
        if (isTypedTimeFullyLegal()) {
        if (isTypedTimeFullyLegal()) {
            if (!mIs24HourView && mTypedTimes.size() <= 3) {
            if (!mIs24HourView && mTypedTimes.size() <= 3) {
+55 −30
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.internal.widget;
import android.content.Context;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Bundle;
import android.util.IntArray;
import android.view.accessibility.*;
import android.view.accessibility.*;
import android.view.MotionEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View;
@@ -26,11 +27,9 @@ import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityNodeProvider;


import java.util.LinkedList;
import java.util.List;

/**
/**
 * ExploreByTouchHelper is a utility class for implementing accessibility
 * ExploreByTouchHelper is a utility class for implementing accessibility
 * support in custom {@link android.view.View}s that represent a collection of View-like
 * support in custom {@link android.view.View}s that represent a collection of View-like
@@ -58,14 +57,16 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
    private static final Rect INVALID_PARENT_BOUNDS = new Rect(
    private static final Rect INVALID_PARENT_BOUNDS = new Rect(
            Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
            Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);


    // Temporary, reusable data structures.
    // Lazily-created temporary data structures used when creating nodes.
    private final Rect mTempScreenRect = new Rect();
    private Rect mTempScreenRect;
    private final Rect mTempParentRect = new Rect();
    private Rect mTempParentRect;
    private final Rect mTempVisibleRect = new Rect();
    private int[] mTempGlobalRect;
    private final int[] mTempGlobalRect = new int[2];

    /** Lazily-created temporary data structure used to compute visibility. */
    private Rect mTempVisibleRect;


    /** View's context **/
    /** Lazily-created temporary data structure used to obtain child IDs. */
    private Context mContext;
    private IntArray mTempArray;


    /** System accessibility manager, used to check state and send events. */
    /** System accessibility manager, used to check state and send events. */
    private final AccessibilityManager mManager;
    private final AccessibilityManager mManager;
@@ -73,6 +74,9 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
    /** View whose internal structure is exposed through this helper. */
    /** View whose internal structure is exposed through this helper. */
    private final View mView;
    private final View mView;


    /** Context of the host view. **/
    private final Context mContext;

    /** Node provider that handles creating nodes and performing actions. */
    /** Node provider that handles creating nodes and performing actions. */
    private ExploreByTouchNodeProvider mNodeProvider;
    private ExploreByTouchNodeProvider mNodeProvider;


@@ -332,11 +336,17 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
        onInitializeAccessibilityNodeInfo(mView, node);
        onInitializeAccessibilityNodeInfo(mView, node);


        // Add the virtual descendants.
        // Add the virtual descendants.
        final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>();
        if (mTempArray == null) {
            mTempArray = new IntArray();
        } else {
            mTempArray.clear();
        }
        final IntArray virtualViewIds = mTempArray;
        getVisibleVirtualViews(virtualViewIds);
        getVisibleVirtualViews(virtualViewIds);


        for (Integer childVirtualViewId : virtualViewIds) {
        final int N = virtualViewIds.size();
            node.addChild(mView, childVirtualViewId);
        for (int i = 0; i < N; i++) {
            node.addChild(mView, virtualViewIds.get(i));
        }
        }


        return node;
        return node;
@@ -371,6 +381,11 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
     * @return An {@link AccessibilityNodeInfo} for the specified item.
     * @return An {@link AccessibilityNodeInfo} for the specified item.
     */
     */
    private AccessibilityNodeInfo createNodeForChild(int virtualViewId) {
    private AccessibilityNodeInfo createNodeForChild(int virtualViewId) {
        ensureTempRects();
        final Rect tempParentRect = mTempParentRect;
        final int[] tempGlobalRect = mTempGlobalRect;
        final Rect tempScreenRect = mTempScreenRect;

        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();
        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();


        // Ensure the client has good defaults.
        // Ensure the client has good defaults.
@@ -387,8 +402,8 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
                    + "populateNodeForVirtualViewId()");
                    + "populateNodeForVirtualViewId()");
        }
        }


        node.getBoundsInParent(mTempParentRect);
        node.getBoundsInParent(tempParentRect);
        if (mTempParentRect.equals(INVALID_PARENT_BOUNDS)) {
        if (tempParentRect.equals(INVALID_PARENT_BOUNDS)) {
            throw new RuntimeException("Callbacks must set parent bounds in "
            throw new RuntimeException("Callbacks must set parent bounds in "
                    + "populateNodeForVirtualViewId()");
                    + "populateNodeForVirtualViewId()");
        }
        }
@@ -411,29 +426,35 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
        // Manage internal accessibility focus state.
        // Manage internal accessibility focus state.
        if (mFocusedVirtualViewId == virtualViewId) {
        if (mFocusedVirtualViewId == virtualViewId) {
            node.setAccessibilityFocused(true);
            node.setAccessibilityFocused(true);
            node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
            node.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
        } else {
        } else {
            node.setAccessibilityFocused(false);
            node.setAccessibilityFocused(false);
            node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
            node.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS);
        }
        }


        // Set the visibility based on the parent bound.
        // Set the visibility based on the parent bound.
        if (intersectVisibleToUser(mTempParentRect)) {
        if (intersectVisibleToUser(tempParentRect)) {
            node.setVisibleToUser(true);
            node.setVisibleToUser(true);
            node.setBoundsInParent(mTempParentRect);
            node.setBoundsInParent(tempParentRect);
        }
        }


        // Calculate screen-relative bound.
        // Calculate screen-relative bound.
        mView.getLocationOnScreen(mTempGlobalRect);
        mView.getLocationOnScreen(tempGlobalRect);
        final int offsetX = mTempGlobalRect[0];
        final int offsetX = tempGlobalRect[0];
        final int offsetY = mTempGlobalRect[1];
        final int offsetY = tempGlobalRect[1];
        mTempScreenRect.set(mTempParentRect);
        tempScreenRect.set(tempParentRect);
        mTempScreenRect.offset(offsetX, offsetY);
        tempScreenRect.offset(offsetX, offsetY);
        node.setBoundsInScreen(mTempScreenRect);
        node.setBoundsInScreen(tempScreenRect);


        return node;
        return node;
    }
    }


    private void ensureTempRects() {
        mTempGlobalRect = new int[2];
        mTempParentRect = new Rect();
        mTempScreenRect = new Rect();
    }

    private boolean performAction(int virtualViewId, int action, Bundle arguments) {
    private boolean performAction(int virtualViewId, int action, Bundle arguments) {
        switch (virtualViewId) {
        switch (virtualViewId) {
            case View.NO_ID:
            case View.NO_ID:
@@ -451,13 +472,13 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
        switch (action) {
        switch (action) {
            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
                return manageFocusForChild(virtualViewId, action, arguments);
                return manageFocusForChild(virtualViewId, action);
            default:
            default:
                return onPerformActionForVirtualView(virtualViewId, action, arguments);
                return onPerformActionForVirtualView(virtualViewId, action, arguments);
        }
        }
    }
    }


    private boolean manageFocusForChild(int virtualViewId, int action, Bundle arguments) {
    private boolean manageFocusForChild(int virtualViewId, int action) {
        switch (action) {
        switch (action) {
            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
                return requestAccessibilityFocus(virtualViewId);
                return requestAccessibilityFocus(virtualViewId);
@@ -503,12 +524,16 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
        }
        }


        // If no portion of the parent is visible, this view is not visible.
        // If no portion of the parent is visible, this view is not visible.
        if (!mView.getLocalVisibleRect(mTempVisibleRect)) {
        if (mTempVisibleRect == null) {
            mTempVisibleRect = new Rect();
        }
        final Rect tempVisibleRect = mTempVisibleRect;
        if (!mView.getLocalVisibleRect(tempVisibleRect)) {
            return false;
            return false;
        }
        }


        // Check if the view intersects the visible portion of the parent.
        // Check if the view intersects the visible portion of the parent.
        return localRect.intersect(mTempVisibleRect);
        return localRect.intersect(tempVisibleRect);
    }
    }


    /**
    /**
@@ -588,7 +613,7 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
     *
     *
     * @param virtualViewIds The list to populate with visible items
     * @param virtualViewIds The list to populate with visible items
     */
     */
    protected abstract void getVisibleVirtualViews(List<Integer> virtualViewIds);
    protected abstract void getVisibleVirtualViews(IntArray virtualViewIds);


    /**
    /**
     * Populates an {@link AccessibilityEvent} with information about the
     * Populates an {@link AccessibilityEvent} with information about the
Loading