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)); + } +}