From b7035f3a4738efdf40dc0ad06f19303ffcd784f7 Mon Sep 17 00:00:00 2001 From: Matthew Ng Date: Thu, 19 Jan 2017 11:04:50 -0800 Subject: [PATCH] Added open, dismiss and split screen as custom accessibility actions In overview with talkback, swiping would select the individual apps instead of the open and dismiss views. The open and dismiss actions are changed to accessibility custom actions with the additional action to split the current app into multi-window mode. Test: manual - used talkback with running all three actions in landscape and portrait Fixes: 29360763 Change-Id: Ie0bec99b849c5754d45577c66b17fa6b0c7e985c --- packages/SystemUI/res/values/config.xml | 7 ++ packages/SystemUI/res/values/strings.xml | 10 ++ .../recents/RecentsConfiguration.java | 43 ++++++- .../events/ui/dragndrop/DragStartEvent.java | 7 ++ .../systemui/recents/views/RecentsView.java | 5 +- .../views/RecentsViewTouchHandler.java | 49 ++------ .../systemui/recents/views/TaskView.java | 6 + .../views/TaskViewAccessibilityDelegate.java | 111 ++++++++++++++++++ 8 files changed, 195 insertions(+), 43 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 50ef392fe0f14..ac86439665b32 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -297,4 +297,11 @@ If < 0, uses the value from HardwarePropertiesManager#getDeviceTemperatures. --> -1 + + + + + + + diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 34a0397eca8bb..4956232d08f69 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -786,6 +786,16 @@ Split Vertical Split Custom + + Dismiss + + Open + + Split screen to the top + + Split screen to the left + + Split screen to the right diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 711f0c679b6de..ff8d4bf68cc6f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -17,12 +17,33 @@ package com.android.systemui.recents; import android.content.Context; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.os.SystemProperties; import com.android.systemui.R; import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.recents.model.TaskStack; + +/** + * Represents the dock regions for each orientation. + */ +class DockRegion { + public static TaskStack.DockState[] PHONE_LANDSCAPE = { + // We only allow docking to the left in landscape for now on small devices + TaskStack.DockState.LEFT + }; + public static TaskStack.DockState[] PHONE_PORTRAIT = { + // We only allow docking to the top for now on small devices + TaskStack.DockState.TOP + }; + public static TaskStack.DockState[] TABLET_LANDSCAPE = { + TaskStack.DockState.LEFT, + TaskStack.DockState.RIGHT + }; + public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT; +} /** * Application resources that can be retrieved from the application context and are not specifically @@ -63,12 +84,14 @@ public class RecentsConfiguration { // Recents will layout task views in a grid mode when there's enough space in the screen. public boolean isGridEnabled; + private final Context mAppContext; + public RecentsConfiguration(Context context) { // Load only resources that can not change after the first load either through developer // settings or via multi window SystemServicesProxy ssp = Recents.getSystemServices(); - Context appContext = context.getApplicationContext(); - Resources res = appContext.getResources(); + mAppContext = context.getApplicationContext(); + Resources res = mAppContext.getResources(); fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows); svelteLevel = res.getInteger(R.integer.recents_svelte_level); isGridEnabled = SystemProperties.getBoolean("ro.recents.grid", false); @@ -86,4 +109,20 @@ public class RecentsConfiguration { public RecentsActivityLaunchState getLaunchState() { return mLaunchState; } + + /** + * Returns the preferred dock states for the current orientation. + * @return a list of dock states for device and its orientation + */ + public TaskStack.DockState[] getDockStatesForCurrentOrientation() { + boolean isLandscape = mAppContext.getResources().getConfiguration().orientation == + Configuration.ORIENTATION_LANDSCAPE; + RecentsConfiguration config = Recents.getConfiguration(); + if (config.isLargeScreen) { + return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT; + } else { + return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT; + } + } + } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java index b368bd353ad84..e57fa2d86a66f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java @@ -30,10 +30,17 @@ public class DragStartEvent extends EventBus.Event { public final Task task; public final TaskView taskView; public final Point tlOffset; + public final boolean isUserTouchInitiated; public DragStartEvent(Task task, TaskView taskView, Point tlOffset) { + this(task, taskView, tlOffset, true); + } + + public DragStartEvent(Task task, TaskView taskView, Point tlOffset, + boolean isUserTouchInitiated) { this.task = task; this.taskView = taskView; this.tlOffset = tlOffset; + this.isUserTouchInitiated = isUserTouchInitiated; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index ee79330f816a2..8f9c457c255ba 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -454,7 +454,7 @@ public class RecentsView extends FrameLayout { } public final void onBusEvent(DragStartEvent event) { - updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(), + updateVisibleDockRegions(Recents.getConfiguration().getDockStatesForCurrentOrientation(), true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, TaskStack.DockState.NONE.viewState.hintTextAlpha, true /* animateAlpha */, false /* animateBounds */); @@ -471,7 +471,8 @@ public class RecentsView extends FrameLayout { public final void onBusEvent(DragDropTargetChangedEvent event) { if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) { - updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(), + updateVisibleDockRegions( + Recents.getConfiguration().getDockStatesForCurrentOrientation(), true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, TaskStack.DockState.NONE.viewState.hintTextAlpha, true /* animateAlpha */, true /* animateBounds */); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java index d3ec9845856a4..db0c95eb59979 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java @@ -28,7 +28,6 @@ import android.view.ViewDebug; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; import com.android.systemui.recents.events.activity.HideRecentsEvent; @@ -44,25 +43,6 @@ import com.android.systemui.recents.model.TaskStack; import java.util.ArrayList; -/** - * Represents the dock regions for each orientation. - */ -class DockRegion { - public static TaskStack.DockState[] PHONE_LANDSCAPE = { - // We only allow docking to the left in landscape for now on small devices - TaskStack.DockState.LEFT - }; - public static TaskStack.DockState[] PHONE_PORTRAIT = { - // We only allow docking to the top for now on small devices - TaskStack.DockState.TOP - }; - public static TaskStack.DockState[] TABLET_LANDSCAPE = { - TaskStack.DockState.LEFT, - TaskStack.DockState.RIGHT - }; - public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT; -} - /** * Handles touch events for a RecentsView. */ @@ -110,20 +90,6 @@ public class RecentsViewTouchHandler { mDropTargets.add(target); } - /** - * Returns the preferred dock states for the current orientation. - */ - public TaskStack.DockState[] getDockStatesForCurrentOrientation() { - boolean isLandscape = mRv.getResources().getConfiguration().orientation == - Configuration.ORIENTATION_LANDSCAPE; - RecentsConfiguration config = Recents.getConfiguration(); - if (config.isLargeScreen) { - return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT; - } else { - return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT; - } - } - /** * Returns the set of visible dock states for this current drag. */ @@ -162,10 +128,14 @@ public class RecentsViewTouchHandler { mRv.getLocationInWindow(recentsViewLocation); mTaskViewOffset.set(mTaskView.getLeft() - recentsViewLocation[0] + event.tlOffset.x, mTaskView.getTop() - recentsViewLocation[1] + event.tlOffset.y); - float x = mDownPos.x - mTaskViewOffset.x; - float y = mDownPos.y - mTaskViewOffset.y; - mTaskView.setTranslationX(x); - mTaskView.setTranslationY(y); + + // Change space coordinates relative to the view to RecentsView when user initiates a touch + if (event.isUserTouchInitiated) { + float x = mDownPos.x - mTaskViewOffset.x; + float y = mDownPos.y - mTaskViewOffset.y; + mTaskView.setTranslationX(x); + mTaskView.setTranslationY(y); + } mVisibleDockStates.clear(); if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask() @@ -176,7 +146,8 @@ public class RecentsViewTouchHandler { EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent()); } else { // Add the dock state drop targets (these take priority) - TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation(); + TaskStack.DockState[] dockStates = Recents.getConfiguration() + .getDockStatesForCurrentOrientation(); for (TaskStack.DockState dockState : dockStates) { registerDropTargetForCurrentDrag(dockState); dockState.update(mRv.getContext()); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 36d5f838a0705..3e4316f44facf 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -183,6 +183,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks } setOutlineProvider(mViewBounds); setOnLongClickListener(this); + setAccessibilityDelegate(new TaskViewAccessibilityDelegate(this)); } /** Set callback */ @@ -256,6 +257,11 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks } } + @Override + public void addChildrenForAccessibility(ArrayList outChildren) { + // Prevent any children from being focusable during talkback + } + @Override public boolean hasOverlappingRendering() { return false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java new file mode 100644 index 0000000000000..759daf1e0f124 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2017 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.systemui.recents.views; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Point; +import android.os.Bundle; +import android.util.SparseArray; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; + +import com.android.systemui.R; +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; +import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; +import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.recents.model.TaskStack; + +public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate { + private static final String TAG = "TaskViewAccessibilityDelegate"; + + private final TaskView mTaskView; + + protected static final int OPEN = R.id.action_open; + protected static final int DIMISS = R.id.action_dimiss; + protected static final int SPLIT_TASK_TOP = R.id.action_split_task_to_top; + protected static final int SPLIT_TASK_LEFT = R.id.action_split_task_to_left; + protected static final int SPLIT_TASK_RIGHT = R.id.action_split_task_to_right; + + protected final SparseArray mActions = new SparseArray<>(); + + public TaskViewAccessibilityDelegate(TaskView taskView) { + mTaskView = taskView; + Context context = taskView.getContext(); + mActions.put(OPEN, new AccessibilityAction(OPEN, + context.getString(R.string.recents_accessibility_open))); + mActions.put(DIMISS, new AccessibilityAction(DIMISS, + context.getString(R.string.recents_accessibility_dismissed))); + mActions.put(SPLIT_TASK_TOP, new AccessibilityAction(SPLIT_TASK_TOP, + context.getString(R.string.recents_accessibility_split_screen_top))); + mActions.put(SPLIT_TASK_LEFT, new AccessibilityAction(SPLIT_TASK_LEFT, + context.getString(R.string.recents_accessibility_split_screen_left))); + mActions.put(SPLIT_TASK_RIGHT, new AccessibilityAction(SPLIT_TASK_RIGHT, + context.getString(R.string.recents_accessibility_split_screen_right))); + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + info.addAction(mActions.get(OPEN)); + info.addAction(mActions.get(DIMISS)); + if (ActivityManager.supportsSplitScreenMultiWindow() + && !Recents.getSystemServices().hasDockedTask()) { + TaskStack.DockState[] dockStates = Recents.getConfiguration() + .getDockStatesForCurrentOrientation(); + for (TaskStack.DockState dockState: dockStates) { + if (dockState == TaskStack.DockState.TOP) { + info.addAction(mActions.get(SPLIT_TASK_TOP)); + } else if (dockState == TaskStack.DockState.LEFT) { + info.addAction(mActions.get(SPLIT_TASK_LEFT)); + } else if (dockState == TaskStack.DockState.RIGHT) { + info.addAction(mActions.get(SPLIT_TASK_RIGHT)); + } + } + } + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (action == OPEN) { + mTaskView.onClick(host); + } else if (action == DIMISS) { + mTaskView.dismissTask(); + } else if (action == SPLIT_TASK_TOP) { + simulateDragIntoMultiwindow(TaskStack.DockState.TOP); + } else if (action == SPLIT_TASK_LEFT) { + simulateDragIntoMultiwindow(TaskStack.DockState.LEFT); + } else if (action == SPLIT_TASK_RIGHT) { + simulateDragIntoMultiwindow(TaskStack.DockState.RIGHT); + } else { + return super.performAccessibilityAction(host, action, args); + } + return true; + } + + /** Simulate a user drag event to split the screen to the respected side */ + private void simulateDragIntoMultiwindow(TaskStack.DockState dockState) { + int orientation = Utilities.getAppConfiguration(mTaskView.getContext()).orientation; + EventBus.getDefault().send(new DragStartEvent(mTaskView.getTask(), mTaskView, + new Point(0,0), false /* isUserTouchInitiated */)); + EventBus.getDefault().send(new DragEndEvent(mTaskView.getTask(), mTaskView, dockState)); + } +}