<commit_before>/*
 * Copyright 2018 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 androidx.customview.widget;

import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityRecord;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.SparseArrayCompat;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.ViewCompat.FocusDirection;
import androidx.core.view.ViewCompat.FocusRealDirection;
import androidx.core.view.ViewParentCompat;
import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
import androidx.core.view.accessibility.AccessibilityRecordCompat;

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

/**
 * ExploreByTouchHelper is a utility class for implementing accessibility
 * support in custom {@link View}s that represent a collection of View-like
 * logical items. It extends {@link AccessibilityNodeProviderCompat} and
 * simplifies many aspects of providing information to accessibility services
 * and managing accessibility focus.
 * <p>
 * Clients should override abstract methods on this class and attach it to the
 * host view using {@link ViewCompat#setAccessibilityDelegate}:
 * <p>
 * <pre>
 * class MyCustomView extends View {
 *     private MyVirtualViewHelper mVirtualViewHelper;
 *
 *     public MyCustomView(Context context, ...) {
 *         ...
 *         mVirtualViewHelper = new MyVirtualViewHelper(this);
 *         ViewCompat.setAccessibilityDelegate(this, mVirtualViewHelper);
 *     }
 *
 *     &#64;Override
 *     public boolean dispatchHoverEvent(MotionEvent event) {
 *       return mHelper.dispatchHoverEvent(this, event)
 *           || super.dispatchHoverEvent(event);
 *     }
 *
 *     &#64;Override
 *     public boolean dispatchKeyEvent(KeyEvent event) {
 *       return mHelper.dispatchKeyEvent(event)
 *           || super.dispatchKeyEvent(event);
 *     }
 *
 *     &#64;Override
 *     public boolean onFocusChanged(boolean gainFocus, int direction,
 *         Rect previouslyFocusedRect) {
 *       super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
 *       mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
 *     }
 * }
 * mAccessHelper = new MyExploreByTouchHelper(someView);
 * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
 * </pre>
 */
public abstract class ExploreByTouchHelper extends AccessibilityDelegateCompat {
    /** Virtual node identifier value for invalid nodes. */
    public static final int INVALID_ID = Integer.MIN_VALUE;

    /** Virtual node identifier value for the host view's node. */
    public static final int HOST_ID = View.NO_ID;

    /** Default class name used for virtual views. */
    private static final String DEFAULT_CLASS_NAME = "android.view.View";

    /** Default bounds used to determine if the client didn't set any. */
    private static final Rect INVALID_PARENT_BOUNDS = new Rect(
            Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);

    // Temporary, reusable data structures.
    private final Rect mTempScreenRect = new Rect();
    private final Rect mTempParentRect = new Rect();
    private final Rect mTempVisibleRect = new Rect();
    private final int[] mTempGlobalRect = new int[2];

    /** System accessibility manager, used to check state and send events. */
    private final AccessibilityManager mManager;

    /** View whose internal structure is exposed through this helper. */
    private final View mHost;

    /** Virtual node provider used to expose logical structure to services. */
    private MyNodeProvider mNodeProvider;

    /** Identifier for the virtual view that holds accessibility focus. */
    private int mAccessibilityFocusedVirtualViewId = INVALID_ID;

    /** Identifier for the virtual view that holds keyboard focus. */
    private int mKeyboardFocusedVirtualViewId = INVALID_ID;

    /** Identifier for the virtual view that is currently hovered. */
    private int mHoveredVirtualViewId = INVALID_ID;

    /**
     * Constructs a new helper that can expose a virtual view hierarchy for the
     * specified host view.
     *
     * @param host view whose virtual view hierarchy is exposed by this helper
     */
    public ExploreByTouchHelper(@NonNull View host) {
        if (host == null) {
            throw new IllegalArgumentException("View may not be null");
        }

        mHost = host;

        final Context context = host.getContext();
        mManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);

        // Host view must be focusable so that we can delegate to virtual
        // views.
        host.setFocusable(true);
        if (ViewCompat.getImportantForAccessibility(host)
                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            ViewCompat.setImportantForAccessibility(
                    host, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
        }
    }

    @Override
    public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
        if (mNodeProvider == null) {
            mNodeProvider = new MyNodeProvider();
        }
        return mNodeProvider;
    }

    /**
     * Delegates hover events from the host view.
     * <p>
     * Dispatches hover {@link MotionEvent}s to the virtual view hierarchy when
     * the Explore by Touch feature is enabled.
     * <p>
     * This method should be called by overriding the host view's
     * {@link View#dispatchHoverEvent(MotionEvent)} method:
     * <pre>&#64;Override
     * public boolean dispatchHoverEvent(MotionEvent event) {
     *   return mHelper.dispatchHoverEvent(this, event)
     *       || super.dispatchHoverEvent(event);
     * }
     * </pre>
     *
     * @param event The hover event to dispatch to the virtual view hierarchy.
     * @return Whether the hover event was handled.
     */
    public final boolean dispatchHoverEvent(@NonNull MotionEvent event) {
        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
            return false;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_HOVER_MOVE:
            case MotionEvent.ACTION_HOVER_ENTER:
                final int virtualViewId = getVirtualViewAt(event.getX(), event.getY());
                updateHoveredVirtualView(virtualViewId);
                return (virtualViewId != INVALID_ID);
            case MotionEvent.ACTION_HOVER_EXIT:
                if (mAccessibilityFocusedVirtualViewId != INVALID_ID) {
                    updateHoveredVirtualView(INVALID_ID);
                    return true;
                }
                return false;
            default:
                return false;
        }
    }

    /**
     * Delegates key events from the host view.
     * <p>
     * This method should be called by overriding the host view's
     * {@link View#dispatchKeyEvent(KeyEvent)} method:
     * <pre>&#64;Override
     * public boolean dispatchKeyEvent(KeyEvent event) {
     *   return mHelper.dispatchKeyEvent(event)
     *       || super.dispatchKeyEvent(event);
     * }
     * </pre>
     */
    public final boolean dispatchKeyEvent(@NonNull KeyEvent event) {
        boolean handled = false;

        final int action = event.getAction();
        if (action != KeyEvent.ACTION_UP) {<commit_msg>Properly manage explore by touch helper state<commit_after>/*
 * Copyright 2018 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 androidx.customview.widget;

import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityRecord;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.SparseArrayCompat;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.ViewCompat.FocusDirection;
import androidx.core.view.ViewCompat.FocusRealDirection;
import androidx.core.view.ViewParentCompat;
import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
import androidx.core.view.accessibility.AccessibilityRecordCompat;

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

/**
 * ExploreByTouchHelper is a utility class for implementing accessibility
 * support in custom {@link View}s that represent a collection of View-like
 * logical items. It extends {@link AccessibilityNodeProviderCompat} and
 * simplifies many aspects of providing information to accessibility services
 * and managing accessibility focus.
 * <p>
 * Clients should override abstract methods on this class and attach it to the
 * host view using {@link ViewCompat#setAccessibilityDelegate}:
 * <p>
 * <pre>
 * class MyCustomView extends View {
 *     private MyVirtualViewHelper mVirtualViewHelper;
 *
 *     public MyCustomView(Context context, ...) {
 *         ...
 *         mVirtualViewHelper = new MyVirtualViewHelper(this);
 *         ViewCompat.setAccessibilityDelegate(this, mVirtualViewHelper);
 *     }
 *
 *     &#64;Override
 *     public boolean dispatchHoverEvent(MotionEvent event) {
 *       return mHelper.dispatchHoverEvent(this, event)
 *           || super.dispatchHoverEvent(event);
 *     }
 *
 *     &#64;Override
 *     public boolean dispatchKeyEvent(KeyEvent event) {
 *       return mHelper.dispatchKeyEvent(event)
 *           || super.dispatchKeyEvent(event);
 *     }
 *
 *     &#64;Override
 *     public boolean onFocusChanged(boolean gainFocus, int direction,
 *         Rect previouslyFocusedRect) {
 *       super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
 *       mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
 *     }
 * }
 * mAccessHelper = new MyExploreByTouchHelper(someView);
 * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
 * </pre>
 */
public abstract class ExploreByTouchHelper extends AccessibilityDelegateCompat {
    /** Virtual node identifier value for invalid nodes. */
    public static final int INVALID_ID = Integer.MIN_VALUE;

    /** Virtual node identifier value for the host view's node. */
    public static final int HOST_ID = View.NO_ID;

    /** Default class name used for virtual views. */
    private static final String DEFAULT_CLASS_NAME = "android.view.View";

    /** Default bounds used to determine if the client didn't set any. */
    private static final Rect INVALID_PARENT_BOUNDS = new Rect(
            Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);

    // Temporary, reusable data structures.
    private final Rect mTempScreenRect = new Rect();
    private final Rect mTempParentRect = new Rect();
    private final Rect mTempVisibleRect = new Rect();
    private final int[] mTempGlobalRect = new int[2];

    /** System accessibility manager, used to check state and send events. */
    private final AccessibilityManager mManager;

    /** View whose internal structure is exposed through this helper. */
    private final View mHost;

    /** Virtual node provider used to expose logical structure to services. */
    private MyNodeProvider mNodeProvider;

    /** Identifier for the virtual view that holds accessibility focus. */
    private int mAccessibilityFocusedVirtualViewId = INVALID_ID;

    /** Identifier for the virtual view that holds keyboard focus. */
    private int mKeyboardFocusedVirtualViewId = INVALID_ID;

    /** Identifier for the virtual view that is currently hovered. */
    private int mHoveredVirtualViewId = INVALID_ID;

    /**
     * Constructs a new helper that can expose a virtual view hierarchy for the
     * specified host view.
     *
     * @param host view whose virtual view hierarchy is exposed by this helper
     */
    public ExploreByTouchHelper(@NonNull View host) {
        if (host == null) {
            throw new IllegalArgumentException("View may not be null");
        }

        mHost = host;

        final Context context = host.getContext();
        mManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);

        // Host view must be focusable so that we can delegate to virtual
        // views.
        host.setFocusable(true);
        if (ViewCompat.getImportantForAccessibility(host)
                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            ViewCompat.setImportantForAccessibility(
                    host, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
        }
    }

    @Override
    public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
        if (mNodeProvider == null) {
            mNodeProvider = new MyNodeProvider();
        }
        return mNodeProvider;
    }

    /**
     * Delegates hover events from the host view.
     * <p>
     * Dispatches hover {@link MotionEvent}s to the virtual view hierarchy when
     * the Explore by Touch feature is enabled.
     * <p>
     * This method should be called by overriding the host view's
     * {@link View#dispatchHoverEvent(MotionEvent)} method:
     * <pre>&#64;Override
     * public boolean dispatchHoverEvent(MotionEvent event) {
     *   return mHelper.dispatchHoverEvent(this, event)
     *       || super.dispatchHoverEvent(event);
     * }
     * </pre>
     *
     * @param event The hover event to dispatch to the virtual view hierarchy.
     * @return Whether the hover event was handled.
     */
    public final boolean dispatchHoverEvent(@NonNull MotionEvent event) {
        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
            return false;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_HOVER_MOVE:
            case MotionEvent.ACTION_HOVER_ENTER:
                final int virtualViewId = getVirtualViewAt(event.getX(), event.getY());
                updateHoveredVirtualView(virtualViewId);
                return (virtualViewId != INVALID_ID);
            case MotionEvent.ACTION_HOVER_EXIT:
                if (mHoveredVirtualViewId != INVALID_ID) {
                    updateHoveredVirtualView(INVALID_ID);
                    return true;
                }
                return false;
            default:
                return false;
        }
    }

    /**
     * Delegates key events from the host view.
     * <p>
     * This method should be called by overriding the host view's
     * {@link View#dispatchKeyEvent(KeyEvent)} method:
     * <pre>&#64;Override
     * public boolean dispatchKeyEvent(KeyEvent event) {
     *   return mHelper.dispatchKeyEvent(event)
     *       || super.dispatchKeyEvent(event);
     * }
     * </pre>
     */
    public final boolean dispatchKeyEvent(@NonNull KeyEvent event) {
        boolean handled = false;

        final int action = event.getAction();
        if (action != KeyEvent.ACTION_UP) {
{'fraction_non_alphanumeric': 0.08175913235206483, 'fraction_numerical': 0.0022644657648531077, 'mean_word_length': 3.1488257107540174, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6101359', 'n_tokens_mistral': 4396, 'n_tokens_neox': 4270, 'n_words': 1330}