import android.view.View;
public interface RecentsComponent {
- void showRecents(boolean triggeredFromAltTab, boolean fromHome, View statusBarView);
+ void showRecents(boolean triggeredFromAltTab, boolean fromHome);
void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
- void toggleRecents(Display display, int layoutDirection, View statusBarView);
+ void toggleRecents(Display display);
void preloadRecents();
void cancelPreloadingRecents();
void showNextAffiliatedTask();
void preloadRecents();
void cancelPreloadingRecents();
void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate,
- boolean reloadTasks, boolean fromHome);
+ boolean reloadTasks, boolean fromHome, int recentsGrowTarget);
void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
- void toggleRecents();
+ void toggleRecents(int recentsGrowTarget);
void onConfigurationChanged();
void dockTopTask(int topTaskId, int dragMode, int stackCreateMode,
in Rect initialBounds);
import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
-import android.util.MutableBoolean;
import android.view.Display;
-import android.view.View;
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.tv.RecentsTvImpl;
+import com.android.systemui.stackdivider.Divider;
import java.util.ArrayList;
public final static int EVENT_BUS_PRIORITY = 1;
public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
+ public final static int RECENTS_GROW_TARGET_INVALID = -1;
// Purely for experimentation
private final static String RECENTS_OVERRIDE_SYSPROP_KEY = "persist.recents_override_pkg";
* Shows the Recents.
*/
@Override
- public void showRecents(boolean triggeredFromAltTab, boolean fromHome, View statusBarView) {
+ public void showRecents(boolean triggeredFromAltTab, boolean fromHome) {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
if (!isUserSetup()) {
return;
}
+ int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
- true /* animate */, false /* reloadTasks */, fromHome);
+ true /* animate */, false /* reloadTasks */, fromHome, recentsGrowTarget);
} else {
if (mSystemToUserCallbacks != null) {
IRecentsNonSystemUserCallbacks callbacks =
if (callbacks != null) {
try {
callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
- true /* animate */, false /* reloadTasks */, fromHome);
+ true /* animate */, false /* reloadTasks */, fromHome,
+ recentsGrowTarget);
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
* Toggles the Recents activity.
*/
@Override
- public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
+ public void toggleRecents(Display display) {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
if (!isUserSetup()) {
return;
}
+ int growTarget = getComponent(Divider.class).getView().growsRecents();
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.toggleRecents();
+ mImpl.toggleRecents(growTarget);
} else {
if (mSystemToUserCallbacks != null) {
IRecentsNonSystemUserCallbacks callbacks =
mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
if (callbacks != null) {
try {
- callbacks.toggleRecents();
+ callbacks.toggleRecents(growTarget);
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.ViewConfiguration;
+import android.view.WindowManager;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.policy.DockedDividerUtils;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.views.TaskStackViewScroller;
import com.android.systemui.recents.views.TaskViewHeader;
import com.android.systemui.recents.views.TaskViewTransform;
+import com.android.systemui.stackdivider.DividerView;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
// When this fires, then the user has not released alt-tab for at least
// FAST_ALT_TAB_DELAY_MS milliseconds
showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
- false /* reloadTasks */, false /* fromHome */);
+ false /* reloadTasks */, false /* fromHome */,
+ DividerView.INVALID_RECENTS_GROW_TARGET);
}
});
}
public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
- boolean animate, boolean launchedWhileDockingTask, boolean fromHome) {
+ boolean animate, boolean launchedWhileDockingTask, boolean fromHome,
+ int growTarget) {
mTriggeredFromAltTab = triggeredFromAltTab;
mDraggingInRecents = draggingInRecents;
mLaunchedWhileDocking = launchedWhileDockingTask;
ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
MutableBoolean isTopTaskHome = new MutableBoolean(true);
if (topTask == null || !ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
- startRecentsActivity(topTask, isTopTaskHome.value || fromHome, animate);
+ startRecentsActivity(topTask, isTopTaskHome.value || fromHome, animate, growTarget);
}
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Failed to launch RecentsActivity", e);
triggeredFromHomeKey));
}
- public void toggleRecents() {
+ public void toggleRecents(int growTarget) {
// Skip this toggle if we are already waiting to trigger recents via alt-tab
if (mFastAltTabTrigger.isDozing()) {
return;
}
// Otherwise, start the recents activity
- startRecentsActivity(topTask, isTopTaskHome.value, true /* animate */);
+ startRecentsActivity(topTask, isTopTaskHome.value, true /* animate */, growTarget);
// Only close the other system windows if we are actually showing recents
ssp.sendCloseSystemWindows(BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
// At this point, we don't know anything about the stack state. So only calculate
// the dimensions of the thumbnail that we need for the transition into Recents, but
// do not draw it until we construct the activity options when we start Recents
- updateHeaderBarLayout(stack);
+ updateHeaderBarLayout(stack, null /* window rect override*/);
}
}
}
dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
false /* animate */,
true /* launchedWhileDockingTask*/,
- false /* fromHome */);
+ false /* fromHome */,
+ DividerView.INVALID_RECENTS_GROW_TARGET);
}
}
* since the last call, it will attempt to re-measure and layout the header bar to the new size.
*
* @param stack the stack to initialize the stack layout with
+ * @param windowRectOverride the rectangle to use when calculating the stack state which can
+ * be different from the current window rect if recents is resizing
+ * while being launched
*/
- private void updateHeaderBarLayout(TaskStack stack) {
+ private void updateHeaderBarLayout(TaskStack stack, Rect windowRectOverride) {
SystemServicesProxy ssp = Recents.getSystemServices();
Rect systemInsets = new Rect();
ssp.getStableInsets(systemInsets);
- Rect windowRect = ssp.getWindowRect();
+ Rect windowRect = windowRectOverride != null
+ ? new Rect(windowRectOverride)
+ : ssp.getWindowRect();
// When docked, the nav bar insets are consumed and the activity is measured without insets.
// However, the window bounds include the insets, so we need to subtract them here to make
// them identical.
* Creates the activity options for an app->recents transition.
*/
private ActivityOptions getThumbnailTransitionActivityOptions(
- ActivityManager.RunningTaskInfo topTask, TaskStackView stackView) {
+ ActivityManager.RunningTaskInfo topTask, TaskStackView stackView,
+ Rect windowOverrideRect) {
if (topTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
ArrayList<Task> tasks = stackView.getStack().getStackTasks();
Task task = tasks.get(i);
if (task.isFreeformTask()) {
mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
- stackScroller.getStackScroll(), mTmpTransform, null);
+ stackScroller.getStackScroll(), mTmpTransform, null,
+ windowOverrideRect);
Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform,
mThumbTransitionBitmapCache);
Rect toTaskRect = new Rect();
} else {
// Update the destination rect
Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
+ TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask,
+ windowOverrideRect);
Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform,
mThumbTransitionBitmapCache);
if (thumbnail != null) {
* Returns the transition rect for the given task id.
*/
private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView,
- Task runningTaskOut) {
+ Task runningTaskOut, Rect windowOverrideRect) {
// Find the running task in the TaskStack
TaskStack stack = stackView.getStack();
Task launchTask = stack.getLaunchTarget();
stackView.updateLayoutAlgorithm(true /* boundScroll */);
stackView.updateToInitialState();
stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
- stackView.getScroller().getStackScroll(), mTmpTransform, null);
+ stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
return mTmpTransform;
}
* Shows the recents activity
*/
protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- boolean isTopTaskHome, boolean animate) {
+ boolean isTopTaskHome, boolean animate, int growTarget) {
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
preloadIcon(topTask);
// Update the header bar if necessary
- updateHeaderBarLayout(stack);
+ Rect windowOverrideRect = getWindowRectOverride(growTarget);
+ updateHeaderBarLayout(stack, windowOverrideRect);
// Prepare the dummy stack for the transition
TaskStackLayoutAlgorithm.VisibilityReport stackVr =
ActivityOptions opts;
if (useThumbnailTransition) {
// Try starting with a thumbnail transition
- opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView);
+ opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView,
+ windowOverrideRect);
} else {
// If there is no thumbnail transition, but is launching from home into recents, then
// use a quick home transition
mLastToggleTime = SystemClock.elapsedRealtime();
}
+ private Rect getWindowRectOverride(int growTarget) {
+ if (growTarget == DividerView.INVALID_RECENTS_GROW_TARGET) {
+ return null;
+ }
+ Rect result = new Rect();
+ Rect displayRect = Recents.getSystemServices().getDisplayRect();
+ DockedDividerUtils.calculateBoundsForPosition(growTarget, WindowManager.DOCKED_BOTTOM,
+ result, displayRect.width(), displayRect.height(),
+ Recents.getSystemServices().getDockedDividerSize(mContext));
+ return result;
+ }
+
/**
* Starts the recents activity.
*/
@Override
public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate,
- boolean reloadTasks, boolean fromHome) throws RemoteException {
+ boolean reloadTasks, boolean fromHome, int growTarget)
+ throws RemoteException {
SomeArgs args = SomeArgs.obtain();
args.argi1 = triggeredFromAltTab ? 1 : 0;
args.argi2 = draggingInRecents ? 1 : 0;
args.argi3 = animate ? 1 : 0;
args.argi4 = reloadTasks ? 1 : 0;
args.argi5 = fromHome ? 1 : 0;
+ args.argi6 = growTarget;
mHandler.sendMessage(mHandler.obtainMessage(MSG_SHOW_RECENTS, args));
}
}
@Override
- public void toggleRecents() throws RemoteException {
- mHandler.sendEmptyMessage(MSG_TOGGLE_RECENTS);
+ public void toggleRecents(int growTarget) throws RemoteException {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_TOGGLE_RECENTS, growTarget));
}
@Override
case MSG_SHOW_RECENTS:
SomeArgs args = (SomeArgs) msg.obj;
mImpl.showRecents(args.argi1 != 0, args.argi2 != 0, args.argi3 != 0,
- args.argi4 != 0, args.argi5 != 0);
+ args.argi4 != 0, args.argi5 != 0, args.argi6);
break;
case MSG_HIDE_RECENTS:
mImpl.hideRecents(msg.arg1 != 0, msg.arg2 != 0);
break;
case MSG_TOGGLE_RECENTS:
- mImpl.toggleRecents();
+ mImpl.toggleRecents(msg.arg1);
break;
case MSG_ON_CONFIGURATION_CHANGED:
mImpl.onConfigurationChanged();
--- /dev/null
+/*
+ * Copyright (C) 2016 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.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Sent when recents is about to grow in multi-window mode when entering recents.
+ */
+public class RecentsGrowingEvent extends EventBus.Event {
+
+}
@Override
protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- boolean isTopTaskHome, boolean animate) {
+ boolean isTopTaskHome, boolean animate, int growTarget) {
RecentsTaskLoader loader = Recents.getTaskLoader();
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
} else {
mTmpTransform.fillIn(taskView);
- stackLayout.transformToScreenCoordinates(mTmpTransform);
+ stackLayout.transformToScreenCoordinates(mTmpTransform,
+ null /* windowOverrideRect */);
specs.add(composeAnimationSpec(stackView, taskView, mTmpTransform,
true /* addHeaderBitmap */));
}
specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect));
} else {
mTmpTransform.fillIn(taskView);
- stackLayout.transformToScreenCoordinates(mTmpTransform);
+ stackLayout.transformToScreenCoordinates(mTmpTransform,
+ null /* windowOverrideRect */);
specs.add(composeAnimationSpec(stackView, tv, mTmpTransform,
true /* addHeaderBitmap */));
}
* Like {@link #getStackTransform}, but in screen coordinates
*/
public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
- TaskViewTransform transformOut, TaskViewTransform frontTransform) {
+ TaskViewTransform transformOut, TaskViewTransform frontTransform,
+ Rect windowOverrideRect) {
TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
transformOut, frontTransform, true /* forceUpdate */,
false /* ignoreTaskOverrides */);
- return transformToScreenCoordinates(transform);
+ return transformToScreenCoordinates(transform, windowOverrideRect);
}
/**
- * Transforms the given {@param transformOut} to the screen coordinates.
+ * Transforms the given {@param transformOut} to the screen coordinates, overriding the current
+ * window rectangle with {@param windowOverrideRect} if non-null.
*/
- public TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut) {
- Rect windowRect = Recents.getSystemServices().getWindowRect();
+ public TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut,
+ Rect windowOverrideRect) {
+ Rect windowRect = windowOverrideRect != null
+ ? windowOverrideRect
+ : Recents.getSystemServices().getWindowRect();
transformOut.rect.offset(windowRect.left, windowRect.top);
return transformOut;
}
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
private TaskViewTransform mTmpTransform = new TaskViewTransform();
private ArrayList<TaskViewTransform> mTmpTaskTransforms = new ArrayList<>();
private int[] mTmpIntPair = new int[2];
+ private boolean mResetToInitialStateWhenResized;
+ private int mLastWidth;
+ private int mLastHeight;
// A convenience update listener to request updating clipping of tasks
private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
// If this is the first layout, then scroll to the front of the stack, then update the
// TaskViews with the stack so that we can lay them out
- if (mAwaitingFirstLayout || mInitialState != INITIAL_STATE_UPDATE_NONE) {
- if (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY) {
+ boolean resetToInitialState = (width != mLastWidth || height != mLastHeight)
+ && mResetToInitialStateWhenResized;
+ if (mAwaitingFirstLayout || mInitialState != INITIAL_STATE_UPDATE_NONE
+ || resetToInitialState) {
+ if (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY || resetToInitialState) {
updateToInitialState();
+ mResetToInitialStateWhenResized = false;
}
if (!mAwaitingFirstLayout) {
mInitialState = INITIAL_STATE_UPDATE_NONE;
}
setMeasuredDimension(width, height);
+ mLastWidth = width;
+ mLastHeight = height;
mInMeasureLayout = false;
}
}
}
+ public final void onBusEvent(RecentsGrowingEvent event) {
+ mResetToInitialStateWhenResized = true;
+ }
+
public void reloadOnConfigurationChange() {
mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.MutableInt;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.GestureDetector;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
+import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.stackdivider.events.StartedDragingEvent;
import com.android.systemui.stackdivider.events.StoppedDragingEvent;
static final long TOUCH_ANIMATION_DURATION = 150;
static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
+ public static final int INVALID_RECENTS_GROW_TARGET = -1;
+
private static final int LOG_VALUE_RESIZE_50_50 = 0;
private static final int LOG_VALUE_RESIZE_DOCKED_SMALLER = 1;
private static final int LOG_VALUE_RESIZE_DOCKED_LARGER = 2;
private static final int LOG_VALUE_UNDOCK_MAX_DOCKED = 0;
private static final int LOG_VALUE_UNDOCK_MAX_OTHER = 1;
-
private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
private static final boolean SWAPPING_ENABLED = false;
mBackground.getRight(), mBackground.getBottom(), Op.UNION);
}
+ /**
+ * Checks whether recents will grow when invoked. This happens in multi-window when recents is
+ * very small. When invoking recents, we shrink the docked stack so recents has more space.
+ *
+ * @return the position of the divider when recents grows, or
+ * {@link #INVALID_RECENTS_GROW_TARGET} if recents won't grow
+ */
+ public int growsRecents() {
+ boolean result = mGrowRecents
+ && mWindowManagerProxy.getDockSide() == WindowManager.DOCKED_TOP
+ && getCurrentPosition() == getSnapAlgorithm().getLastSplitTarget().position;
+ if (result) {
+ return getSnapAlgorithm().getMiddleTarget().position;
+ } else {
+ return INVALID_RECENTS_GROW_TARGET;
+ }
+ }
+
public final void onBusEvent(RecentsActivityStartingEvent recentsActivityStartingEvent) {
if (mGrowRecents && getWindowManagerProxy().getDockSide() == WindowManager.DOCKED_TOP
&& getCurrentPosition() == getSnapAlgorithm().getLastSplitTarget().position) {
if (mGrowAfterRecentsDrawn) {
mGrowAfterRecentsDrawn = false;
updateDockSide();
- stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250,
+ EventBus.getDefault().send(new RecentsGrowingEvent());
+ stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 336,
Interpolators.FAST_OUT_SLOW_IN);
}
}
protected void showRecents(boolean triggeredFromAltTab, boolean fromHome) {
if (mRecents != null) {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
- mRecents.showRecents(triggeredFromAltTab, fromHome, getStatusBarView());
+ mRecents.showRecents(triggeredFromAltTab, fromHome);
}
}
protected void toggleRecents() {
if (mRecents != null) {
- mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
+ mRecents.toggleRecents(mDisplay);
}
}
final Task task = w.getTask();
// We got resized, so block all updates until we got the new surface.
- if (w.mResizedWhileNotDragResizing) {
+ if (w.mResizedWhileNotDragResizing && !w.isGoneForLayoutLw()) {
return;
}