Snap for 7921329 from 36705e6039 to sc-v2-release

Change-Id: Ice4379600fbb72aaffd5cb6a4f5b2d75a1c45a2b
This commit is contained in:
Android Build Coastguard Worker
2021-11-17 00:07:39 +00:00
12 changed files with 516 additions and 331 deletions
@@ -15,18 +15,10 @@
*/
package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED;
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.ColorInt;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -44,28 +36,17 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
@@ -77,82 +58,15 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
private final BaseQuickstepLauncher mLauncher;
private final AnimatedFloat mIconAlignmentForResumedState =
new AnimatedFloat(this::onIconAlignmentRatioChanged);
private final AnimatedFloat mIconAlignmentForGestureState =
new AnimatedFloat(this::onIconAlignmentRatioChanged);
private final AnimatedFloat mIconAlignmentForLauncherState =
new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
this::onStashedInAppChanged;
private final StateManager.StateListener<LauncherState> mStateListener =
new StateManager.StateListener<LauncherState>() {
private Animator mAnimator;
@Override
public void onStateTransitionStart(LauncherState toState) {
// Stash animation from going to launcher should be already handled in
// createAnimToLauncher.
TaskbarStashController controller = mControllers.taskbarStashController;
long duration = TASKBAR_STASH_DURATION;
controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
toState.isTaskbarStashed());
Animator stashAnimator = controller.applyStateWithoutStart(duration);
if (stashAnimator != null) {
if (mAnimator != null) {
mAnimator.cancel();
}
PendingAnimation pendingAnimation = new PendingAnimation(duration);
pendingAnimation.add(stashAnimator);
pendingAnimation.setFloat(mIconAlignmentForLauncherState,
AnimatedFloat.VALUE, toState.isTaskbarStashed() ? 0 : 1,
FAST_OUT_SLOW_IN);
pendingAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
mTargetStateOverrideForStateTransition = toState;
// Copy hotseat alpha over to taskbar icons
mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
mLauncher.getHotseat().setIconsAlpha(0);
}
@Override
public void onAnimationEnd(Animator animator) {
if (toState.isTaskbarStashed()) {
// Reset hotseat alpha to default
mLauncher.getHotseat().setIconsAlpha(1);
}
mTargetStateOverrideForStateTransition = null;
mAnimator = null;
}
});
mAnimator = pendingAnimation.buildAnim();
mAnimator.start();
}
}
@Override
public void onStateTransitionComplete(LauncherState finalState) {
TaskbarStashController controller = mControllers.taskbarStashController;
controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
finalState.isTaskbarStashed());
controller.applyState();
}
};
// Initialized in init.
private TaskbarControllers mControllers;
private AnimatedFloat mTaskbarBackgroundAlpha;
private AnimatedFloat mTaskbarOverrideBackgroundAlpha;
private AlphaProperty mIconAlphaForHome;
private boolean mIsAnimatingToLauncherViaResume;
private boolean mIsAnimatingToLauncherViaGesture;
private TaskbarKeyguardController mKeyguardController;
private LauncherState mTargetStateOverride = null;
private LauncherState mTargetStateOverrideForStateTransition = null;
private final TaskbarLauncherStateController
mTaskbarLauncherStateController = new TaskbarLauncherStateController();
private final DeviceProfile.OnDeviceProfileChangeListener mProfileChangeListener =
new DeviceProfile.OnDeviceProfileChangeListener() {
@@ -171,37 +85,26 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
protected void init(TaskbarControllers taskbarControllers) {
mControllers = taskbarControllers;
mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
.getTaskbarBackgroundAlpha();
mTaskbarLauncherStateController.init(mControllers, mLauncher);
mTaskbarOverrideBackgroundAlpha = mControllers.taskbarDragLayerController
.getOverrideBackgroundAlpha();
MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
mLauncher.setTaskbarUIController(this);
mKeyguardController = taskbarControllers.taskbarKeyguardController;
onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */);
mIconAlignmentForResumedState.finishAnimation();
onIconAlignmentRatioChanged();
onStashedInAppChanged(mLauncher.getDeviceProfile());
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
mLauncher.getStateManager().addStateListener(mStateListener);
mLauncher.addOnDeviceProfileChangeListener(mProfileChangeListener);
}
@Override
protected void onDestroy() {
onLauncherResumedOrPaused(false);
mIconAlignmentForResumedState.finishAnimation();
mIconAlignmentForGestureState.finishAnimation();
mIconAlignmentForLauncherState.finishAnimation();
mTaskbarLauncherStateController.onDestroy();
mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
mLauncher.getStateManager().removeStateListener(mStateListener);
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.setTaskbarUIController(null);
mLauncher.removeOnDeviceProfileChangeListener(mProfileChangeListener);
updateTaskTransitionSpec(true);
@@ -209,11 +112,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
@Override
protected boolean isTaskbarTouchable() {
return !isAnimatingToLauncher();
}
private boolean isAnimatingToLauncher() {
return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
return !mTaskbarLauncherStateController.isAnimatingToLauncher();
}
@Override
@@ -240,24 +139,9 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
}
}
long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
if (fromInit) {
// Since we are creating the starting state, we don't have a state to animate from, so
// set our state immediately.
duration = 0;
}
ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue(
getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
.setDuration(duration);
anim.addListener(AnimatorListeners.forEndCallback(
() -> mIsAnimatingToLauncherViaResume = false));
anim.start();
mIsAnimatingToLauncherViaResume = isResumed;
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
stashController.applyState(duration);
mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
mTaskbarLauncherStateController.applyState(
fromInit ? 0 : QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
}
/**
@@ -268,77 +152,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
*/
public Animator createAnimToLauncher(@NonNull LauncherState toState,
@NonNull RecentsAnimationCallbacks callbacks, long duration) {
AnimatorSet animatorSet = new AnimatorSet();
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
toState.isTaskbarStashed());
if (toState.isTaskbarStashed()) {
animatorSet.play(stashController.applyStateWithoutStart(duration));
} else {
animatorSet.play(mIconAlignmentForGestureState
.animateToValue(1)
.setDuration(duration));
}
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
mTargetStateOverride = null;
animator.removeListener(this);
}
@Override
public void onAnimationStart(Animator animator) {
mTargetStateOverride = toState;
mIsAnimatingToLauncherViaGesture = true;
stashController.updateStateForFlag(FLAG_IN_APP, false);
stashController.applyState(duration);
}
});
TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks);
callbacks.addListener(listener);
RecentsView recentsView = mLauncher.getOverviewPanel();
recentsView.setTaskLaunchListener(() -> {
listener.endGestureStateOverride(true);
callbacks.removeListener(listener);
});
return animatorSet;
}
private float getCurrentIconAlignmentRatio() {
return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
}
private float getCurrentIconAlignmentRatioForLauncherState() {
return mIconAlignmentForLauncherState.value;
}
private void onIconAlignmentRatioChangedForStateTransition() {
onIconAlignmentRatioChanged(
mTargetStateOverrideForStateTransition != null
? mTargetStateOverrideForStateTransition
: mLauncher.getStateManager().getState(),
this::getCurrentIconAlignmentRatioForLauncherState);
}
private void onIconAlignmentRatioChanged() {
onIconAlignmentRatioChanged(mTargetStateOverride != null ? mTargetStateOverride
: mLauncher.getStateManager().getState(), this::getCurrentIconAlignmentRatio);
}
private void onIconAlignmentRatioChanged(LauncherState state,
Supplier<Float> alignmentSupplier) {
if (mControllers == null) {
return;
}
float alignment = alignmentSupplier.get();
mControllers.taskbarViewController.setLauncherIconAlignment(
alignment, mLauncher.getDeviceProfile());
mTaskbarBackgroundAlpha.updateValue(1 - alignment);
setIconAlpha(state, alignment);
return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
}
/**
@@ -358,20 +172,6 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
return mControllers.taskbarActivityContext.getDragLayer();
}
private void setIconAlpha(LauncherState state, float progress) {
if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
// If the hotseat icons are visible, then switch taskbar in last frame
setTaskbarViewVisible(progress < 1);
} else {
mIconAlphaForHome.setValue(1 - progress);
}
}
private void setTaskbarViewVisible(boolean isVisible) {
mIconAlphaForHome.setValue(isVisible ? 1 : 0);
mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
}
@Override
protected void onStashedInAppChanged() {
onStashedInAppChanged(mLauncher.getDeviceProfile());
@@ -451,35 +251,4 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
mLauncher.logAppLaunch(mControllers.taskbarActivityContext.getStatsLogManager(), item,
instanceId);
}
private final class TaskBarRecentsAnimationListener implements RecentsAnimationListener {
private final RecentsAnimationCallbacks mCallbacks;
TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) {
mCallbacks = callbacks;
}
@Override
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
endGestureStateOverride(true);
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
endGestureStateOverride(!controller.getFinishTargetIsLauncher());
}
private void endGestureStateOverride(boolean finishedToApp) {
mCallbacks.removeListener(this);
mIsAnimatingToLauncherViaGesture = false;
mIconAlignmentForGestureState
.animateToValue(0)
.start();
TaskbarStashController controller = mControllers.taskbarStashController;
controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
controller.applyState();
}
}
}
@@ -0,0 +1,395 @@
/*
* 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.taskbar;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import androidx.annotation.NonNull;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import java.util.HashMap;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
* the task bar accordingly.
*/
public class TaskbarLauncherStateController {
public static final int FLAG_RESUMED = 1 << 0;
public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1;
public static final int FLAG_TRANSITION_STATE_START_STASHED = 1 << 2;
public static final int FLAG_TRANSITION_STATE_COMMITTED_STASHED = 1 << 3;
private final AnimatedFloat mIconAlignmentForResumedState =
new AnimatedFloat(this::onIconAlignmentRatioChanged);
private final AnimatedFloat mIconAlignmentForGestureState =
new AnimatedFloat(this::onIconAlignmentRatioChanged);
private final AnimatedFloat mIconAlignmentForLauncherState =
new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
private TaskbarControllers mControllers;
private AnimatedFloat mTaskbarBackgroundAlpha;
private MultiValueAlpha.AlphaProperty mIconAlphaForHome;
private BaseQuickstepLauncher mLauncher;
private int mPrevState;
private int mState;
private LauncherState mTargetStateOverride = null;
private LauncherState mTargetStateOverrideForStateTransition = null;
private boolean mIsAnimatingToLauncherViaGesture;
private boolean mIsAnimatingToLauncherViaResume;
private final StateManager.StateListener<LauncherState> mStateListener =
new StateManager.StateListener<LauncherState>() {
@Override
public void onStateTransitionStart(LauncherState toState) {
mTargetStateOverrideForStateTransition = toState;
updateStateForFlag(FLAG_TRANSITION_STATE_START_STASHED,
toState.isTaskbarStashed());
applyState();
}
@Override
public void onStateTransitionComplete(LauncherState finalState) {
updateStateForFlag(FLAG_TRANSITION_STATE_COMMITTED_STASHED,
finalState.isTaskbarStashed());
applyState();
}
};
public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) {
mControllers = controllers;
mLauncher = launcher;
mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
.getTaskbarBackgroundAlpha();
MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
mIconAlphaForHome.setConsumer(
(Consumer<Float>) alpha -> mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1));
mIconAlignmentForResumedState.finishAnimation();
onIconAlignmentRatioChanged();
mLauncher.getStateManager().addStateListener(mStateListener);
}
public void onDestroy() {
mIconAlignmentForResumedState.finishAnimation();
mIconAlignmentForGestureState.finishAnimation();
mIconAlignmentForLauncherState.finishAnimation();
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.getStateManager().removeStateListener(mStateListener);
}
public Animator createAnimToLauncher(@NonNull LauncherState toState,
@NonNull RecentsAnimationCallbacks callbacks, long duration) {
// If going to overview, stash the task bar
// If going home, align the icons to hotseat
AnimatorSet animatorSet = new AnimatorSet();
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
toState.isTaskbarStashed());
stashController.updateStateForFlag(FLAG_IN_APP, false);
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true);
animatorSet.play(stashController.applyStateWithoutStart(duration));
animatorSet.play(applyState(duration, false));
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
mTargetStateOverride = null;
animator.removeListener(this);
}
@Override
public void onAnimationStart(Animator animator) {
mTargetStateOverride = toState;
}
});
TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks);
callbacks.addListener(listener);
RecentsView recentsView = mLauncher.getOverviewPanel();
recentsView.setTaskLaunchListener(() -> {
listener.endGestureStateOverride(true);
callbacks.removeListener(listener);
});
return animatorSet;
}
public boolean isAnimatingToLauncher() {
return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
}
/**
* Updates the proper flag to change the state of the task bar.
*
* Note that this only updates the flag. {@link #applyState()} needs to be called separately.
*
* @param flag The flag to update.
* @param enabled Whether to enable the flag
*/
public void updateStateForFlag(int flag, boolean enabled) {
if (enabled) {
mState |= flag;
} else {
mState &= ~flag;
}
}
private boolean hasAnyFlag(int flagMask) {
return hasAnyFlag(mState, flagMask);
}
private boolean hasAnyFlag(int flags, int flagMask) {
return (flags & flagMask) != 0;
}
public void applyState() {
applyState(TASKBAR_STASH_DURATION);
}
public void applyState(long duration) {
applyState(duration, true);
}
public Animator applyState(boolean start) {
return applyState(TASKBAR_STASH_DURATION, start);
}
public Animator applyState(long duration, boolean start) {
Animator animator = null;
if (mPrevState != mState) {
int changedFlags = mPrevState ^ mState;
animator = onStateChangeApplied(changedFlags, duration, start);
mPrevState = mState;
}
return animator;
}
private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
AnimatorSet animatorSet = new AnimatorSet();
if (hasAnyFlag(changedFlags, FLAG_RESUMED)) {
boolean isResumed = isResumed();
ObjectAnimator anim = mIconAlignmentForResumedState
.animateToValue(getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
.setDuration(duration);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mIsAnimatingToLauncherViaResume = false;
}
@Override
public void onAnimationStart(Animator animation) {
mIsAnimatingToLauncherViaResume = isResumed;
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
stashController.applyState(duration);
}
});
animatorSet.play(anim);
}
if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)) {
boolean isRecentsAnimationRunning = isRecentsAnimationRunning();
Animator animator = mIconAlignmentForGestureState
.animateToValue(isRecentsAnimationRunning ? 1 : 0);
if (isRecentsAnimationRunning) {
animator.setDuration(duration);
}
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mIsAnimatingToLauncherViaGesture = false;
}
@Override
public void onAnimationStart(Animator animation) {
mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning();
}
});
animatorSet.play(animator);
}
if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_START_STASHED)) {
playStateTransitionAnim(isTransitionStateStartStashed(), animatorSet, duration,
false /* committed */);
}
if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_COMMITTED_STASHED)) {
playStateTransitionAnim(isTransitionStateCommittedStashed(), animatorSet, duration,
true /* committed */);
}
if (start) {
animatorSet.start();
}
return animatorSet;
}
private void playStateTransitionAnim(boolean isTransitionStateStashed,
AnimatorSet animatorSet, long duration, boolean committed) {
TaskbarStashController controller = mControllers.taskbarStashController;
controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
isTransitionStateStashed);
Animator stashAnimator = controller.applyStateWithoutStart(duration);
if (stashAnimator != null) {
stashAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (isTransitionStateStashed && committed) {
// Reset hotseat alpha to default
mLauncher.getHotseat().setIconsAlpha(1);
}
mTargetStateOverrideForStateTransition = null;
}
@Override
public void onAnimationStart(Animator animation) {
mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
}
});
animatorSet.play(stashAnimator);
animatorSet.play(mIconAlignmentForLauncherState.animateToValue(
getCurrentIconAlignmentRatioForLauncherState(),
isTransitionStateStashed ? 0 : 1));
} else {
mTargetStateOverrideForStateTransition = null;
}
}
private boolean isResumed() {
return (mState & FLAG_RESUMED) != 0;
}
private boolean isRecentsAnimationRunning() {
return (mState & FLAG_RECENTS_ANIMATION_RUNNING) != 0;
}
private boolean isTransitionStateStartStashed() {
return (mState & FLAG_TRANSITION_STATE_START_STASHED) != 0;
}
private boolean isTransitionStateCommittedStashed() {
return (mState & FLAG_TRANSITION_STATE_COMMITTED_STASHED) != 0;
}
private void onIconAlignmentRatioChangedForStateTransition() {
onIconAlignmentRatioChanged(
mTargetStateOverrideForStateTransition != null
? mTargetStateOverrideForStateTransition
: mLauncher.getStateManager().getState(),
this::getCurrentIconAlignmentRatioForLauncherState);
}
private void onIconAlignmentRatioChanged() {
onIconAlignmentRatioChanged(mTargetStateOverride != null ? mTargetStateOverride
: mLauncher.getStateManager().getState(), this::getCurrentIconAlignmentRatio);
}
private void onIconAlignmentRatioChanged(LauncherState state,
Supplier<Float> alignmentSupplier) {
if (mControllers == null) {
return;
}
float alignment = alignmentSupplier.get();
mControllers.taskbarViewController.setLauncherIconAlignment(
alignment, mLauncher.getDeviceProfile());
mTaskbarBackgroundAlpha.updateValue(1 - alignment);
setIconAlpha(state, alignment);
}
private float getCurrentIconAlignmentRatio() {
return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
}
private float getCurrentIconAlignmentRatioForLauncherState() {
return mIconAlignmentForLauncherState.value;
}
private void setIconAlpha(LauncherState state, float progress) {
if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
// If the hotseat icons are visible, then switch taskbar in last frame
setTaskbarViewVisible(progress < 1);
} else {
mIconAlphaForHome.setValue(1 - progress);
}
}
private void setTaskbarViewVisible(boolean isVisible) {
mIconAlphaForHome.setValue(isVisible ? 1 : 0);
}
private final class TaskBarRecentsAnimationListener implements
RecentsAnimationCallbacks.RecentsAnimationListener {
private final RecentsAnimationCallbacks mCallbacks;
TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) {
mCallbacks = callbacks;
}
@Override
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
endGestureStateOverride(true);
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
endGestureStateOverride(!controller.getFinishTargetIsLauncher());
}
private void endGestureStateOverride(boolean finishedToApp) {
mCallbacks.removeListener(this);
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, false);
applyState();
TaskbarStashController controller = mControllers.taskbarStashController;
controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
controller.applyState();
}
}
}
@@ -117,11 +117,12 @@ public class OverviewCommandHelper {
mPendingCommands.clear();
}
@Nullable
private TaskView getNextTask(RecentsView view) {
final TaskView runningTaskView = view.getRunningTaskView();
if (runningTaskView == null) {
return view.getTaskViewCount() > 0 ? view.getTaskViewAt(0) : null;
return view.getTaskViewAt(0);
} else {
final TaskView nextTask = view.getNextTaskView();
return nextTask != null ? nextTask : runningTaskView;
@@ -256,8 +257,8 @@ public class OverviewCommandHelper {
// Ensure that recents view has focus so that it receives the followup key inputs
TaskView taskView = rv.getNextTaskView();
if (taskView == null) {
if (rv.getTaskViewCount() > 0) {
taskView = rv.getTaskViewAt(0);
taskView = rv.getTaskViewAt(0);
if (taskView != null) {
taskView.requestFocus();
} else {
rv.requestFocus();
@@ -383,7 +383,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
protected final RecentsOrientedState mOrientationState;
protected final BaseActivityInterface<STATE_TYPE, ACTIVITY_TYPE> mSizeStrategy;
@Nullable
protected RecentsAnimationController mRecentsAnimationController;
@Nullable
protected SurfaceTransactionApplier mSyncTransactionApplier;
protected int mTaskWidth;
protected int mTaskHeight;
@@ -394,12 +396,15 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// mTaskGridVerticalDiff and mTopBottomRowHeightDiff summed together provides the top
// position for bottom row of grid tasks.
@Nullable
protected RemoteTargetHandle[] mRemoteTargetHandles;
protected final Rect mLastComputedTaskSize = new Rect();
protected final Rect mLastComputedGridSize = new Rect();
protected final Rect mLastComputedGridTaskSize = new Rect();
// How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
@Nullable
protected Float mLastComputedTaskStartPushOutDistance = null;
@Nullable
protected Float mLastComputedTaskEndPushOutDistance = null;
protected boolean mEnableDrawingLiveTile = false;
protected final Rect mTempRect = new Rect();
@@ -454,11 +459,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private final IntSet mTopRowIdSet = new IntSet();
// The GestureEndTarget that is still in progress.
@Nullable
protected GestureState.GestureEndTarget mCurrentGestureEndTarget;
// TODO(b/187528071): Remove these and replace with a real scrim.
private float mColorTint;
private final int mTintingColor;
@Nullable
private ObjectAnimator mTintingAnimator;
private int mOverScrollShift = 0;
@@ -542,6 +549,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private int mTaskViewIdCount;
private final int[] INVALID_TASK_IDS = new int[]{-1, -1};
protected boolean mRunningTaskTileHidden;
@Nullable
private Task[] mTmpRunningTasks;
protected int mFocusedTaskViewId = -1;
@@ -556,7 +564,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private int mDownX;
private int mDownY;
@Nullable
private PendingAnimation mPendingAnimation;
@Nullable
private LayoutTransition mLayoutTransition;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -581,7 +591,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private final Point mLastMeasureSize = new Point();
private final int mEmptyMessagePadding;
private boolean mShowEmptyMessage;
@Nullable
private OnEmptyMessageUpdatedListener mOnEmptyMessageUpdatedListener;
@Nullable
private Layout mEmptyTextLayout;
/**
@@ -596,8 +608,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* ensure this View doesn't go back into the {@link #mTaskViewPool},
* see {@link #onViewRemoved(View)}
*/
@Nullable
private TaskView mSplitHiddenTaskView;
@Nullable
private TaskView mSecondSplitHiddenTaskView;
@Nullable
private StagedSplitBounds mSplitBoundsConfig;
private final Toast mSplitToast = Toast.makeText(getContext(),
R.string.toast_split_select_app, Toast.LENGTH_SHORT);
@@ -613,12 +628,15 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* removed from recentsView
*/
private int mSplitHiddenTaskViewIndex;
@Nullable
private FloatingTaskView mFirstFloatingTaskView;
@Nullable
private FloatingTaskView mSecondFloatingTaskView;
/**
* The task to be removed and immediately re-added. Should not be added to task pool.
*/
@Nullable
private TaskView mMovingTaskView;
private OverviewActionsView mActionsView;
@@ -638,10 +656,12 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
};
@Nullable
private RunnableList mSideTaskLaunchCallback;
@Nullable
private TaskLaunchListener mTaskLaunchListener;
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
BaseActivityInterface sizeStrategy) {
super(context, attrs, defStyleAttr);
setEnableFreeScroll(true);
@@ -782,6 +802,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
@Override
@Nullable
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
if (mHandleTaskStackChanges) {
TaskView taskView = getTaskViewByTaskId(taskId);
@@ -1017,6 +1038,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
}
@Nullable
private TaskView getLastGridTaskView() {
return getLastGridTaskView(getTopRowIdArray(), getBottomRowIdArray());
}
@@ -1070,6 +1092,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
return getScrollForPage(taskIndex) == getPagedOrientationHandler().getPrimaryScroll(this);
}
/**
* Returns a {@link TaskView} that has taskId matching {@code taskId} or null if no match.
*/
@Nullable
public TaskView getTaskViewByTaskId(int taskId) {
if (taskId == -1) {
return null;
@@ -1309,6 +1335,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
return;
}
mLoadPlanEverApplied = true;
if (taskGroups == null || taskGroups.isEmpty()) {
removeTasksViewsAndClearAllButton();
onTaskStackUpdated();
@@ -1411,7 +1438,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
resetTaskVisuals();
onTaskStackUpdated();
updateEnabledOverlays();
mLoadPlanEverApplied = true;
}
private boolean isModal() {
@@ -1920,6 +1946,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
return getTaskViewFromTaskViewId(mFocusedTaskViewId);
}
@Nullable
private TaskView getTaskViewFromTaskViewId(int taskViewId) {
if (taskViewId == -1) {
return null;
@@ -3088,12 +3115,17 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
} else if (dismissedIndex < pageToSnapTo || pageToSnapTo == taskCount - 1) {
pageToSnapTo--;
}
boolean isHomeTaskDismissed = dismissedTaskView == getHomeTaskView();
removeViewInLayout(dismissedTaskView);
mTopRowIdSet.remove(dismissedTaskViewId);
if (taskCount == 1) {
removeViewInLayout(mClearAllButton);
startHome();
if (isHomeTaskDismissed) {
updateEmptyMessage();
} else {
startHome();
}
} else {
// Update focus task and its size.
if (finalIsFocusedTaskDismissed) {
@@ -4371,12 +4403,15 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
}
public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
/**
* Finish recents animation.
*/
public void finishRecentsAnimation(boolean toRecents, @Nullable Runnable onFinishComplete) {
finishRecentsAnimation(toRecents, true /* shouldPip */, onFinishComplete);
}
public void finishRecentsAnimation(boolean toRecents, boolean shouldPip,
Runnable onFinishComplete) {
@Nullable Runnable onFinishComplete) {
// TODO(b/197232424#comment#10) Move this back into onRecentsAnimationComplete(). Maybe?
cleanupRemoteTargets();
if (!toRecents && ENABLE_QUICKSTEP_LIVE_TILE.get()) {
@@ -4939,10 +4974,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private static class PinnedStackAnimationListener<T extends BaseActivity> extends
IPipAnimationListener.Stub {
@Nullable
private T mActivity;
@Nullable
private RecentsView mRecentsView;
public void setActivityAndRecentsView(T activity, RecentsView recentsView) {
public void setActivityAndRecentsView(@Nullable T activity,
@Nullable RecentsView recentsView) {
mActivity = activity;
mRecentsView = recentsView;
}
@@ -459,15 +459,36 @@ public class TaskThumbnailView extends View {
float availableHeight = surfaceHeight
- (thumbnailClipHint.top + thumbnailClipHint.bottom);
if (isRotated) {
float canvasAspect = canvasWidth / (float) canvasHeight;
float availableAspect = availableHeight / availableWidth;
float canvasAspect = canvasWidth / (float) canvasHeight;
float availableAspect = isRotated
? availableHeight / availableWidth
: availableWidth / availableHeight;
boolean isAspectLargelyDifferent = Utilities.isRelativePercentDifferenceGreaterThan(
canvasAspect, availableAspect, 0.1f);
if (isRotated && isAspectLargelyDifferent) {
// Do not rotate thumbnail if it would not improve fit
if (Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
availableAspect, 0.1f)) {
isRotated = false;
isOrientationDifferent = false;
isRotated = false;
isOrientationDifferent = false;
}
if (isAspectLargelyDifferent) {
// Crop letterbox insets if insets isn't already clipped
if (!TaskView.clipLeft(dp)) {
thumbnailClipHint.left = thumbnailData.letterboxInsets.left;
}
if (!TaskView.clipRight(dp)) {
thumbnailClipHint.right = thumbnailData.letterboxInsets.right;
}
if (!TaskView.clipTop(dp)) {
thumbnailClipHint.top = thumbnailData.letterboxInsets.top;
}
if (!TaskView.clipBottom(dp)) {
thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom;
}
availableWidth = surfaceWidth
- (thumbnailClipHint.left + thumbnailClipHint.right);
availableHeight = surfaceHeight
- (thumbnailClipHint.top + thumbnailClipHint.bottom);
}
final float targetW, targetH;
@@ -478,30 +499,25 @@ public class TaskThumbnailView extends View {
targetW = canvasWidth;
targetH = canvasHeight;
}
float canvasAspect = targetW / targetH;
float targetAspect = targetW / targetH;
// Update the clipHint such that
// > the final clipped position has same aspect ratio as requested by canvas
// > the clipped region is within the task insets if possible
// > the clipped region is not scaled up when drawing. If that is not possible
// while staying within the taskInsets, move outside the insets.
// > first fit the width and crop the extra height
// > if that will leave empty space, fit the height and crop the width instead
float croppedWidth = availableWidth;
if (croppedWidth < targetW) {
croppedWidth = Math.min(targetW, surfaceWidth);
}
float croppedHeight = croppedWidth / canvasAspect;
float croppedHeight = croppedWidth / targetAspect;
if (croppedHeight > availableHeight) {
croppedHeight = availableHeight;
if (croppedHeight < targetH) {
croppedHeight = Math.min(targetH, surfaceHeight);
}
croppedWidth = croppedHeight * canvasAspect;
croppedWidth = croppedHeight * targetAspect;
// One last check in case the task aspect radio messed up something
if (croppedWidth > surfaceWidth) {
croppedWidth = surfaceWidth;
croppedHeight = croppedWidth / canvasAspect;
croppedHeight = croppedWidth / targetAspect;
}
}
@@ -383,7 +383,7 @@ public class LauncherProvider extends ContentProvider {
case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
Bundle result = new Bundle();
result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
mOpenHelper.generateNewScreenId());
mOpenHelper.getNewScreenId());
return result;
}
case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
@@ -628,7 +628,6 @@ public class LauncherProvider extends ContentProvider {
private final Context mContext;
private final boolean mForMigration;
private int mMaxItemId = -1;
private int mMaxScreenId = -1;
private boolean mBackupTableExists;
private boolean mHotseatRestoreTableExists;
@@ -672,9 +671,6 @@ public class LauncherProvider extends ContentProvider {
if (mMaxItemId == -1) {
mMaxItemId = initializeMaxItemId(getWritableDatabase());
}
if (mMaxScreenId == -1) {
mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
}
}
@Override
@@ -682,7 +678,6 @@ public class LauncherProvider extends ContentProvider {
if (LOGD) Log.d(TAG, "creating new launcher database");
mMaxItemId = 1;
mMaxScreenId = 0;
addFavoritesTable(db, false);
@@ -1043,36 +1038,19 @@ public class LauncherProvider extends ContentProvider {
public void checkId(ContentValues values) {
int id = values.getAsInteger(Favorites._ID);
mMaxItemId = Math.max(id, mMaxItemId);
Integer screen = values.getAsInteger(Favorites.SCREEN);
Integer container = values.getAsInteger(Favorites.CONTAINER);
if (screen != null && container != null
&& container.intValue() == Favorites.CONTAINER_DESKTOP) {
mMaxScreenId = Math.max(screen, mMaxScreenId);
}
}
private int initializeMaxItemId(SQLiteDatabase db) {
return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s", Favorites._ID, Favorites.TABLE_NAME);
}
// Generates a new ID to use for an workspace screen in your database. This method
// should be only called from the main UI thread. As an exception, we do call it when we
// call the constructor from the worker thread; however, this doesn't extend until after the
// constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
// after that point
public int generateNewScreenId() {
if (mMaxScreenId < 0) {
throw new RuntimeException("Error: max screen id was not initialized");
}
mMaxScreenId += 1;
return mMaxScreenId;
}
private int initializeMaxScreenId(SQLiteDatabase db) {
return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s WHERE %3$s = %4$d AND %1$s >= 0",
// Returns a new ID to use for an workspace screen in your database that is greater than all
// existing screen IDs.
private int getNewScreenId() {
return getMaxId(getWritableDatabase(),
"SELECT MAX(%1$s) FROM %2$s WHERE %3$s = %4$d AND %1$s >= 0",
Favorites.SCREEN, Favorites.TABLE_NAME, Favorites.CONTAINER,
Favorites.CONTAINER_DESKTOP);
Favorites.CONTAINER_DESKTOP) + 1;
}
@Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
@@ -1081,7 +1059,6 @@ public class LauncherProvider extends ContentProvider {
// Ensure that the max ids are initialized
mMaxItemId = initializeMaxItemId(db);
mMaxScreenId = initializeMaxScreenId(db);
return count;
}
}
+4
View File
@@ -1721,6 +1721,10 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
if (mScroller.isFinished()) {
// This was not caused by the scroller, skip it.
return;
}
int newDestinationPage = getDestinationPage();
if (newDestinationPage >= 0 && newDestinationPage != mCurrentScrollOverPage) {
mCurrentScrollOverPage = newDestinationPage;
+5 -5
View File
@@ -869,13 +869,13 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
mWorkspaceScreens.remove(emptyScreenId);
mScreenOrder.removeValue(emptyScreenId);
int newScreenId = -1;
int newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
// Launcher database isn't aware of empty pages that are already bound, so we need to
// skip those IDs manually.
while (newScreenId == -1 || mWorkspaceScreens.containsKey(newScreenId)) {
newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
while (mWorkspaceScreens.containsKey(newScreenId)) {
newScreenId++;
}
mWorkspaceScreens.put(newScreenId, cl);
@@ -16,7 +16,6 @@
package com.android.launcher3.model;
import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
import static com.android.launcher3.WorkspaceLayoutManager.SECOND_SCREEN_ID;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
@@ -300,11 +299,6 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
IntSet screensToExclude = new IntSet();
if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
screensToExclude.add(FIRST_SCREEN_ID);
// On split display we don't want to add the new items onto the second screen.
if (app.getInvariantDeviceProfile().isSplitDisplay) {
screensToExclude.add(SECOND_SCREEN_ID);
}
}
for (int screen = 0; screen < screenCount; screen++) {
@@ -292,7 +292,7 @@ public class ModelWriter {
FileLog.d(TAG, "removing items from db " + items.stream().map(
(item) -> item.getTargetComponent() == null ? ""
: item.getTargetComponent().getPackageName()).collect(
Collectors.joining(",")), new Exception());
Collectors.joining(",")));
notifyDelete(items);
enqueueDeleteRunnable(() -> {
for (ItemInfo item : items) {
@@ -24,6 +24,7 @@ import android.view.View;
import com.android.launcher3.anim.AlphaUpdateListener;
import java.util.Arrays;
import java.util.function.Consumer;
/**
* Utility class to handle separating a single value as a factor of multiple values
@@ -85,6 +86,8 @@ public class MultiValueAlpha {
// Factor of all other alpha channels, only valid if mMyMask is present in mValidMask.
private float mOthers = 1;
private Consumer<Float> mConsumer;
AlphaProperty(int myMask) {
mMyMask = myMask;
}
@@ -109,16 +112,24 @@ public class MultiValueAlpha {
mValidMask = mMyMask;
mValue = value;
mView.setAlpha(mOthers * mValue);
final float alpha = mOthers * mValue;
mView.setAlpha(alpha);
if (mUpdateVisibility) {
AlphaUpdateListener.updateVisibility(mView);
}
if (mConsumer != null) {
mConsumer.accept(mValue);
}
}
public float getValue() {
return mValue;
}
public void setConsumer(Consumer<Float> consumer) {
mConsumer = consumer;
}
@Override
public String toString() {
return Float.toString(mValue);
@@ -86,8 +86,6 @@ public class AddWorkspaceItemsTaskTest {
@Test
public void testFindSpaceForItem_prefers_second() throws Exception {
mIdp.isSplitDisplay = false;
// First screen has only one hole of size 1
int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
@@ -108,24 +106,6 @@ public class AddWorkspaceItemsTaskTest {
.isRegionVacant(spaceFound[1], spaceFound[2], 2, 3));
}
@Test
public void testFindSpaceForItem_prefers_third_on_split_display() throws Exception {
mIdp.isSplitDisplay = true;
// First screen has only one hole of size 1
int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
// Second screen has 2 holes of sizes 3x2 and 2x3
setupWorkspaceWithHoles(nextId, 2, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
int[] spaceFound = newTask().findSpaceForItem(
mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 1, 1);
// For split display, it picks the next screen, even if there is enough space
// on previous screen
assertEquals(2, spaceFound[0]);
assertTrue(mScreenOccupancy.get(spaceFound[0])
.isRegionVacant(spaceFound[1], spaceFound[2], 1, 1));
}
@Test
public void testFindSpaceForItem_adds_new_screen() throws Exception {
// First screen has 2 holes of sizes 3x2 and 2x3