public class

LocationBasedViewTracker

extends java.lang.Object

implements java.lang.Runnable

 java.lang.Object

↳androidx.slice.widget.LocationBasedViewTracker

Gradle dependencies

compile group: 'androidx.slice', name: 'slice-view', version: '1.1.0-alpha02'

  • groupId: androidx.slice
  • artifactId: slice-view
  • version: 1.1.0-alpha02

Artifact androidx.slice:slice-view:1.1.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.slice:slice-view com.android.support:slices-view

Overview

Utility class to track view based on relative location to the parent.

Summary

Methods
public voidonLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)

public voidrun()

public static voidtrackA11yFocus(ViewGroup parent)

Tries to preserve the accessibility focus after the next content change

public static voidtrackInputFocused(ViewGroup parent)

Tries to preserve the input focus after the next content change

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Methods

public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)

public void run()

public static void trackInputFocused(ViewGroup parent)

Tries to preserve the input focus after the next content change

public static void trackA11yFocus(ViewGroup parent)

Tries to preserve the accessibility focus after the next content change

Source

/*
 * Copyright 2019 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.slice.widget;

import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;

import androidx.annotation.RestrictTo;

import java.util.ArrayList;

/**
 * Utility class to track view based on relative location to the parent.
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class LocationBasedViewTracker implements Runnable, View.OnLayoutChangeListener {

    private static final SelectionLogic INPUT_FOCUS = new SelectionLogic() {
        @Override
        public void selectView(View view) {
            view.requestFocus();
        }
    };

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private static final SelectionLogic A11Y_FOCUS = new SelectionLogic() {
        @Override
        public void selectView(View view) {
            view.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
        }
    };

    private final Rect mFocusRect = new Rect();
    private final ViewGroup mParent;
    private final SelectionLogic mSelectionLogic;

    private LocationBasedViewTracker(ViewGroup parent, View selected,
            SelectionLogic selectionLogic) {
        mParent = parent;
        mSelectionLogic = selectionLogic;

        selected.getDrawingRect(mFocusRect);
        parent.offsetDescendantRectToMyCoords(selected, mFocusRect);

        // Request a layout pass immediately after storing the view position. This ensure that we
        // get onLayoutChange before the selected view can change as a result of user interaction.
        mParent.addOnLayoutChangeListener(this);
        mParent.requestLayout();
    }

    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
            int oldTop, int oldRight, int oldBottom) {
        mParent.removeOnLayoutChangeListener(this);
        mParent.post(this);
    }

    @Override
    public void run() {
        ArrayList<View> views = new ArrayList<>();
        mParent.addFocusables(views, View.FOCUS_FORWARD, View.FOCUSABLES_ALL);

        Rect temp = new Rect();
        int oldCloseness = Integer.MAX_VALUE;
        View closestView = null;

        for (View v : views) {
            v.getDrawingRect(temp);
            mParent.offsetDescendantRectToMyCoords(v, temp);
            if (!mFocusRect.intersect(temp)) {
                continue;
            }

            // Find closeness
            int closeness = Math.abs(mFocusRect.left - temp.left)
                    + Math.abs(mFocusRect.right - temp.right)
                    + Math.abs(mFocusRect.top - temp.top)
                    + Math.abs(mFocusRect.bottom - temp.bottom);
            if (oldCloseness > closeness) {
                oldCloseness = closeness;
                closestView = v;
            }
        }
        if (closestView != null) {
            mSelectionLogic.selectView(closestView);
        }
    }

    /**
     * Tries to preserve the input focus after the next content change
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public static void trackInputFocused(ViewGroup parent) {
        View focused = parent.findFocus();
        if (focused != null) {
            new LocationBasedViewTracker(parent, focused, INPUT_FOCUS);
        }
    }

    /**
     * Tries to preserve the accessibility focus after the next content change
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public static void trackA11yFocus(ViewGroup parent) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            return;
        }
        if (!((AccessibilityManager) parent.getContext()
                .getSystemService(Context.ACCESSIBILITY_SERVICE)).isTouchExplorationEnabled()) {
            return;
        }
        ArrayList<View> children = new ArrayList<>();
        parent.addFocusables(children, View.FOCUS_FORWARD, View.FOCUSABLES_ALL);
        View focused = null;
        for (View child : children) {
            if (child.isAccessibilityFocused()) {
                focused = child;
                break;
            }
        }
        if (focused != null) {
            new LocationBasedViewTracker(parent, focused, A11Y_FOCUS);
        }
    }

    /**
     * Interface to control how a view is selected
     */
    private interface SelectionLogic {

        void selectView(View view);
    }
}