mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-20 11:18:21 +00:00
Bug: 232165524 Test: Verified locally Change-Id: I3b4f37bc9914de55330662edd678e6fdd0eafece
200 lines
6.8 KiB
Java
200 lines
6.8 KiB
Java
/*
|
|
* 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.BubbleTextView;
|
|
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.util.Thunk;
|
|
import com.android.launcher3.views.ActivityContext;
|
|
import com.android.launcher3.views.BubbleTextHolder;
|
|
|
|
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)) {
|
|
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) {
|
|
if (host instanceof BubbleTextView) {
|
|
return ((BubbleTextView) host).canShowLongPressPopup();
|
|
} else if (host instanceof BubbleTextHolder) {
|
|
BubbleTextHolder holder = (BubbleTextHolder) host;
|
|
return holder.getBubbleText() != null && holder.getBubbleText().canShowLongPressPopup();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|