mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-03 17:36:49 +00:00
Refactor LauncherAccessibilityDelegate so it can be used outside of Launcher
Bug: 198438631 Test: used talkback on launcher Change-Id: I991320184ad93816c4ba21fb8fcfe12202bfae25
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 com.android.launcher3.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.android.launcher3.DropTarget;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.dragndrop.DragController;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BaseAccessibilityDelegate<T extends Context & ActivityContext>
|
||||
extends View.AccessibilityDelegate implements DragController.DragListener {
|
||||
|
||||
public enum DragType {
|
||||
ICON,
|
||||
FOLDER,
|
||||
WIDGET
|
||||
}
|
||||
|
||||
public static class DragInfo {
|
||||
public DragType dragType;
|
||||
public ItemInfo info;
|
||||
public View item;
|
||||
}
|
||||
|
||||
protected final SparseArray<LauncherAction> mActions = new SparseArray<>();
|
||||
protected final T mContext;
|
||||
|
||||
protected DragInfo mDragInfo = null;
|
||||
|
||||
protected BaseAccessibilityDelegate(T context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(host, info);
|
||||
if (host.getTag() instanceof ItemInfo) {
|
||||
ItemInfo item = (ItemInfo) host.getTag();
|
||||
|
||||
List<LauncherAction> actions = new ArrayList<>();
|
||||
getSupportedActions(host, item, actions);
|
||||
actions.forEach(la -> info.addAction(la.accessibilityAction));
|
||||
|
||||
if (!itemSupportsLongClick(host, item)) {
|
||||
info.setLongClickable(false);
|
||||
info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all the accessibility actions that can be handled.
|
||||
*/
|
||||
protected abstract void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out);
|
||||
|
||||
private boolean itemSupportsLongClick(View host, ItemInfo info) {
|
||||
return PopupContainerWithArrow.canShow(host, info);
|
||||
}
|
||||
|
||||
protected boolean itemSupportsAccessibleDrag(ItemInfo item) {
|
||||
if (item instanceof WorkspaceItemInfo) {
|
||||
// Support the action unless the item is in a context menu.
|
||||
return item.screenId >= 0
|
||||
&& item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
}
|
||||
return (item instanceof LauncherAppWidgetInfo)
|
||||
|| (item instanceof FolderInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(View host, int action, Bundle args) {
|
||||
if ((host.getTag() instanceof ItemInfo)
|
||||
&& performAction(host, (ItemInfo) host.getTag(), action, false)) {
|
||||
return true;
|
||||
}
|
||||
return super.performAccessibilityAction(host, action, args);
|
||||
}
|
||||
|
||||
protected abstract boolean performAction(
|
||||
View host, ItemInfo item, int action, boolean fromKeyboard);
|
||||
|
||||
@Thunk
|
||||
protected void announceConfirmation(String confirmation) {
|
||||
mContext.getDragLayer().announceForAccessibility(confirmation);
|
||||
|
||||
}
|
||||
|
||||
public boolean isInAccessibleDrag() {
|
||||
return mDragInfo != null;
|
||||
}
|
||||
|
||||
public DragInfo getDragInfo() {
|
||||
return mDragInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clickedTarget the actual view that was clicked
|
||||
* @param dropLocation relative to {@param clickedTarget}. If provided, its center is used
|
||||
* as the actual drop location otherwise the views center is used.
|
||||
*/
|
||||
public void handleAccessibleDrop(View clickedTarget, Rect dropLocation,
|
||||
String confirmation) {
|
||||
if (!isInAccessibleDrag()) return;
|
||||
|
||||
int[] loc = new int[2];
|
||||
if (dropLocation == null) {
|
||||
loc[0] = clickedTarget.getWidth() / 2;
|
||||
loc[1] = clickedTarget.getHeight() / 2;
|
||||
} else {
|
||||
loc[0] = dropLocation.centerX();
|
||||
loc[1] = dropLocation.centerY();
|
||||
}
|
||||
|
||||
mContext.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc);
|
||||
mContext.getDragController().completeAccessibleDrag(loc);
|
||||
|
||||
if (!TextUtils.isEmpty(confirmation)) {
|
||||
announceConfirmation(confirmation);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard);
|
||||
|
||||
|
||||
@Override
|
||||
public void onDragEnd() {
|
||||
mContext.getDragController().removeDragListener(this);
|
||||
mDragInfo = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public class LauncherAction {
|
||||
public final int keyCode;
|
||||
public final AccessibilityNodeInfo.AccessibilityAction accessibilityAction;
|
||||
|
||||
private final BaseAccessibilityDelegate<T> mDelegate;
|
||||
|
||||
public LauncherAction(int id, int labelRes, int keyCode) {
|
||||
this.keyCode = keyCode;
|
||||
accessibilityAction = new AccessibilityNodeInfo.AccessibilityAction(
|
||||
id, mContext.getString(labelRes));
|
||||
mDelegate = BaseAccessibilityDelegate.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the action for the provided host
|
||||
*/
|
||||
public boolean invokeFromKeyboard(View host) {
|
||||
if (host != null && host.getTag() instanceof ItemInfo) {
|
||||
return mDelegate.performAction(
|
||||
host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user